Merge "Add JankData Processing Logic" into main
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/OWNERS b/OWNERS
index eb2bfcf..d0a634e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -47,3 +47,4 @@
 per-file BROADCASTS_OWNERS = file:/BROADCASTS_OWNERS
 per-file ADPF_OWNERS = file:/ADPF_OWNERS
 per-file GAME_MANAGER_OWNERS = file:/GAME_MANAGER_OWNERS
+per-file SDK_OWNERS = file:/SDK_OWNERS
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/SDK_OWNERS b/SDK_OWNERS
new file mode 100644
index 0000000..c9ca47a
--- /dev/null
+++ b/SDK_OWNERS
@@ -0,0 +1,6 @@
+amhk@google.com
+kimalexander@google.com
+lus@google.com
+michaelwr@google.com
+nanaasiedu@google.com
+paulduffin@google.com
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 4be16a6..debd850 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -25,10 +25,10 @@
 }
 
 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 {
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/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/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index ee246d8..909a9b3 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
@@ -618,6 +618,26 @@
         return mRunningJob;
     }
 
+    @VisibleForTesting
+    void setRunningJobLockedForTest(JobStatus job) {
+        mRunningJob = job;
+    }
+
+    @VisibleForTesting
+    void setJobParamsLockedForTest(JobParameters params) {
+        mParams = params;
+    }
+
+    @VisibleForTesting
+    void setRunningCallbackLockedForTest(JobCallback callback) {
+        mRunningCallback = callback;
+    }
+
+    @VisibleForTesting
+    void setPendingStopReasonLockedForTest(int stopReason) {
+        mPendingStopReason = stopReason;
+    }
+
     @JobConcurrencyManager.WorkType
     int getRunningJobWorkType() {
         return mRunningJobWorkType;
@@ -773,7 +793,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;
@@ -786,10 +806,11 @@
                 executing = getRunningJobLocked();
             }
             if (executing != null && jobId == executing.getJobId()) {
+                executing.setAbandoned(true);
                 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());
             }
@@ -1364,8 +1385,9 @@
     }
 
     /** Process MSG_TIMEOUT here. */
+    @VisibleForTesting
     @GuardedBy("mLock")
-    private void handleOpTimeoutLocked() {
+    void handleOpTimeoutLocked() {
         switch (mVerb) {
             case VERB_BINDING:
                 // The system may have been too busy. Don't drop the job or trigger an ANR.
@@ -1427,9 +1449,25 @@
                     // Not an error - client ran out of time.
                     Slog.i(TAG, "Client timed out while executing (no jobFinished received)."
                             + " Sending onStop: " + getRunningJobNameLocked());
-                    mParams.setStopReason(JobParameters.STOP_REASON_TIMEOUT,
-                            JobParameters.INTERNAL_STOP_REASON_TIMEOUT, "client timed out");
-                    sendStopMessageLocked("timeout while executing");
+
+                    final JobStatus executing = getRunningJobLocked();
+                    int stopReason = JobParameters.STOP_REASON_TIMEOUT;
+                    int internalStopReason = JobParameters.INTERNAL_STOP_REASON_TIMEOUT;
+                    final StringBuilder stopMessage = new StringBuilder("timeout while executing");
+                    final StringBuilder debugStopReason = new StringBuilder("client timed out");
+
+                    if (android.app.job.Flags.handleAbandonedJobs()
+                            && executing != null && executing.isAbandoned()) {
+                        final String abandonedMessage = " and maybe abandoned";
+                        stopReason = JobParameters.STOP_REASON_TIMEOUT_ABANDONED;
+                        internalStopReason = JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED;
+                        stopMessage.append(abandonedMessage);
+                        debugStopReason.append(abandonedMessage);
+                    }
+
+                    mParams.setStopReason(stopReason,
+                                    internalStopReason, debugStopReason.toString());
+                    sendStopMessageLocked(stopMessage.toString());
                 } else if (nowElapsed >= earliestStopTimeElapsed) {
                     // We've given the app the minimum execution time. See if we should stop it or
                     // let it continue running
@@ -1479,8 +1517,9 @@
      * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
      * VERB_STOPPING.
      */
+    @VisibleForTesting
     @GuardedBy("mLock")
-    private void sendStopMessageLocked(@Nullable String reason) {
+    void sendStopMessageLocked(@Nullable String reason) {
         removeOpTimeOutLocked();
         if (mVerb != VERB_EXECUTING) {
             Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
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/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index e3af1d8..1dc5a71 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -576,6 +576,13 @@
     private String mSystemTraceTag;
 
     /**
+     * Job maybe abandoned by not calling
+     * {@link android.app.job.JobService#jobFinished(JobParameters, boolean)} while
+     * the strong reference to {@link android.app.job.JobParameters} is lost
+     */
+    private boolean mIsAbandoned;
+
+    /**
      * Core constructor for JobStatus instances.  All other ctors funnel down to this one.
      *
      * @param job The actual requested parameters for the job
@@ -725,6 +732,8 @@
         updateNetworkBytesLocked();
 
         updateMediaBackupExemptionStatus();
+
+        mIsAbandoned = false;
     }
 
     /** Copy constructor: used specifically when cloning JobStatus objects for persistence,
@@ -1061,6 +1070,16 @@
         return job.getTraceTag();
     }
 
+    /** Returns if the job maybe abandoned */
+    public boolean isAbandoned() {
+        return mIsAbandoned;
+    }
+
+    /** Set the job maybe abandoned state*/
+    public void setAbandoned(boolean abandoned) {
+        mIsAbandoned = abandoned;
+    }
+
     /** Returns a trace tag using debug information provided by job scheduler service. */
     @NonNull
     public String computeSystemTraceTag() {
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 ff4af69..8bd3ef4 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
@@ -121,6 +121,9 @@
     private static final String ALARM_TAG_CLEANUP = "*job.cleanup*";
     private static final String ALARM_TAG_QUOTA_CHECK = "*job.quota_check*";
 
+    private static final String TRACE_QUOTA_STATE_CHANGED_TAG = "QuotaStateChanged:";
+    private static final String TRACE_QUOTA_STATE_CHANGED_DELIMITER = "#";
+
     private static final int SYSTEM_APP_CHECK_FLAGS =
             PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                     | PackageManager.GET_PERMISSIONS | PackageManager.MATCH_KNOWN_PACKAGES;
@@ -2657,11 +2660,12 @@
                         if (timeRemainingMs <= 50) {
                             // Less than 50 milliseconds left. Start process of shutting down jobs.
                             if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
-                            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-                                Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
-                                        JobSchedulerService.TRACE_TRACK_NAME,
-                                        pkg + "#" + MSG_REACHED_TIME_QUOTA);
-                            }
+                            final StringBuilder traceMsg = new StringBuilder();
+                            traceMsg.append(TRACE_QUOTA_STATE_CHANGED_TAG)
+                                    .append(pkg)
+                                    .append(TRACE_QUOTA_STATE_CHANGED_DELIMITER)
+                                    .append(MSG_REACHED_TIME_QUOTA);
+                            Trace.instant(Trace.TRACE_TAG_POWER, traceMsg.toString());
                             mStateChangedListener.onControllerStateChanged(
                                     maybeUpdateConstraintForPkgLocked(
                                             sElapsedRealtimeClock.millis(),
@@ -2690,11 +2694,12 @@
                                 pkg.userId, pkg.packageName);
                         if (timeRemainingMs <= 0) {
                             if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
-                            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-                                Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
-                                        JobSchedulerService.TRACE_TRACK_NAME,
-                                        pkg + "#" + MSG_REACHED_EJ_TIME_QUOTA);
-                            }
+                            final StringBuilder traceMsg = new StringBuilder();
+                            traceMsg.append(TRACE_QUOTA_STATE_CHANGED_TAG)
+                                    .append(pkg)
+                                    .append(TRACE_QUOTA_STATE_CHANGED_DELIMITER)
+                                    .append(MSG_REACHED_EJ_TIME_QUOTA);
+                            Trace.instant(Trace.TRACE_TAG_POWER, traceMsg.toString());
                             mStateChangedListener.onControllerStateChanged(
                                     maybeUpdateConstraintForPkgLocked(
                                             sElapsedRealtimeClock.millis(),
@@ -2719,11 +2724,12 @@
                             Slog.d(TAG, pkg + " has reached its count quota.");
                         }
 
-                        if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-                            Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
-                                    JobSchedulerService.TRACE_TRACK_NAME,
-                                    pkg + "#" + MSG_REACHED_COUNT_QUOTA);
-                        }
+                        final StringBuilder traceMsg = new StringBuilder();
+                        traceMsg.append(TRACE_QUOTA_STATE_CHANGED_TAG)
+                                .append(pkg)
+                                .append(TRACE_QUOTA_STATE_CHANGED_DELIMITER)
+                                .append(MSG_REACHED_COUNT_QUOTA);
+                        Trace.instant(Trace.TRACE_TAG_POWER, traceMsg.toString());
 
                         mStateChangedListener.onControllerStateChanged(
                                 maybeUpdateConstraintForPkgLocked(
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/boot/boot-image-profile-extra.txt b/boot/boot-image-profile-extra.txt
index 11ca1dc..e31eb3a 100644
--- a/boot/boot-image-profile-extra.txt
+++ b/boot/boot-image-profile-extra.txt
@@ -23,3 +23,4 @@
 # For now, compile all methods in MessageQueue to avoid performance cliffs for
 # flagged/evolving hot code paths. See: b/338098106
 HSPLandroid/os/MessageQueue;->*
+HSPLandroid/os/MessageQueue$*;->*
diff --git a/core/api/current.txt b/core/api/current.txt
index 13e1210..2cb7a30 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -294,7 +294,7 @@
     field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
     field public static final String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
     field public static final String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
-    field @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public static final String SET_BIOMETRIC_DIALOG_ADVANCED = "android.permission.SET_BIOMETRIC_DIALOG_ADVANCED";
+    field public static final String SET_BIOMETRIC_DIALOG_ADVANCED = "android.permission.SET_BIOMETRIC_DIALOG_ADVANCED";
     field public static final String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
     field @Deprecated public static final String SET_PREFERRED_APPLICATIONS = "android.permission.SET_PREFERRED_APPLICATIONS";
     field public static final String SET_PROCESS_LIMIT = "android.permission.SET_PROCESS_LIMIT";
@@ -1006,6 +1006,7 @@
     field public static final int insetRight = 16843192; // 0x10101b8
     field public static final int insetTop = 16843193; // 0x10101b9
     field public static final int installLocation = 16843447; // 0x10102b7
+    field @FlaggedApi("android.security.enable_intent_matching_flags") public static final int intentMatchingFlags;
     field public static final int interactiveUiTimeout = 16844181; // 0x1010595
     field public static final int interpolator = 16843073; // 0x1010141
     field public static final int intro = 16844395; // 0x101066b
@@ -1609,6 +1610,7 @@
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
+    field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int supplementalDescription;
     field public static final int supportedTypes = 16844369; // 0x1010651
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsBatteryGameMode = 16844374; // 0x1010656
@@ -4616,6 +4618,7 @@
     method public void setInheritShowWhenLocked(boolean);
     method public void setIntent(android.content.Intent);
     method @FlaggedApi("android.security.content_uri_permission_apis") public void setIntent(@Nullable android.content.Intent, @Nullable android.app.ComponentCaller);
+    method @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public final void setLimitSystemEducationDialogs(boolean);
     method public void setLocusContext(@Nullable android.content.LocusId, @Nullable android.os.Bundle);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setPictureInPictureParams(@NonNull android.app.PictureInPictureParams);
@@ -8783,7 +8786,8 @@
 
   @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
     method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
-    method public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
+    method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
+    method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
     field public static final int APP_FUNCTION_STATE_DEFAULT = 0; // 0x0
     field public static final int APP_FUNCTION_STATE_DISABLED = 2; // 0x2
@@ -8816,6 +8820,7 @@
 
   @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionResponse implements android.os.Parcelable {
     method public int describeContents();
+    method public int getErrorCategory();
     method @Nullable public String getErrorMessage();
     method @NonNull public android.os.Bundle getExtras();
     method public int getResultCode();
@@ -8825,14 +8830,19 @@
     method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR;
+    field public static final int ERROR_CATEGORY_APP = 3; // 0x3
+    field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
+    field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
+    field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
     field public static final String PROPERTY_RETURN_VALUE = "returnValue";
-    field public static final int RESULT_APP_UNKNOWN_ERROR = 2; // 0x2
-    field public static final int RESULT_CANCELLED = 6; // 0x6
-    field public static final int RESULT_DENIED = 1; // 0x1
-    field public static final int RESULT_DISABLED = 5; // 0x5
-    field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
-    field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
+    field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8
+    field public static final int RESULT_CANCELLED = 2001; // 0x7d1
+    field public static final int RESULT_DENIED = 1000; // 0x3e8
+    field public static final int RESULT_DISABLED = 1002; // 0x3ea
+    field public static final int RESULT_FUNCTION_NOT_FOUND = 1003; // 0x3eb
+    field public static final int RESULT_INVALID_ARGUMENT = 1001; // 0x3e9
     field public static final int RESULT_OK = 0; // 0x0
+    field public static final int RESULT_SYSTEM_ERROR = 2000; // 0x7d0
   }
 
 }
@@ -9218,6 +9228,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 +9675,49 @@
 
 }
 
+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 static android.app.wallpaper.WallpaperDescription readFromStream(@NonNull java.io.InputStream) throws java.io.IOException;
+    method @NonNull public android.app.wallpaper.WallpaperDescription.Builder toBuilder();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    method public void writeToStream(@NonNull java.io.OutputStream) throws java.io.IOException;
+    field @NonNull 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 {
@@ -9790,6 +9844,7 @@
     field public static final int RESIZE_VERTICAL = 2; // 0x2
     field public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; // 0x1
     field public static final int WIDGET_CATEGORY_KEYGUARD = 2; // 0x2
+    field @FlaggedApi("android.appwidget.flags.not_keyguard_category") public static final int WIDGET_CATEGORY_NOT_KEYGUARD = 8; // 0x8
     field public static final int WIDGET_CATEGORY_SEARCHBOX = 4; // 0x4
     field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4; // 0x4
     field public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2; // 0x2
@@ -10872,6 +10927,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";
@@ -10898,6 +10954,7 @@
     field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
     field public static final String RESTRICTIONS_SERVICE = "restrictions";
     field public static final String ROLE_SERVICE = "role";
+    field @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public static final String SATELLITE_SERVICE = "satellite";
     field public static final String SEARCH_SERVICE = "search";
     field @FlaggedApi("android.os.security_state_service") public static final String SECURITY_STATE_SERVICE = "security_state";
     field public static final String SENSOR_SERVICE = "sensor";
@@ -11485,10 +11542,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";
@@ -16704,7 +16761,7 @@
     method public boolean hasGlyph(String);
     method public final boolean isAntiAlias();
     method public final boolean isDither();
-    method public boolean isElegantTextHeight();
+    method @Deprecated @FlaggedApi("com.android.text.flags.deprecate_elegant_text_height_api") public boolean isElegantTextHeight();
     method public final boolean isFakeBoldText();
     method public final boolean isFilterBitmap();
     method public final boolean isLinearText();
@@ -16725,7 +16782,7 @@
     method public void setColor(@ColorLong long);
     method public android.graphics.ColorFilter setColorFilter(android.graphics.ColorFilter);
     method public void setDither(boolean);
-    method public void setElegantTextHeight(boolean);
+    method @Deprecated @FlaggedApi("com.android.text.flags.deprecate_elegant_text_height_api") public void setElegantTextHeight(boolean);
     method public void setEndHyphenEdit(int);
     method public void setFakeBoldText(boolean);
     method public void setFilterBitmap(boolean);
@@ -17318,6 +17375,25 @@
     method public boolean setUseCompositingLayer(boolean, @Nullable android.graphics.Paint);
   }
 
+  @FlaggedApi("com.android.graphics.hwui.flags.runtime_color_filters_blenders") public class RuntimeColorFilter extends android.graphics.ColorFilter {
+    ctor public RuntimeColorFilter(@NonNull String);
+    method public void setColorUniform(@NonNull String, @ColorInt int);
+    method public void setColorUniform(@NonNull String, @ColorLong long);
+    method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color);
+    method public void setFloatUniform(@NonNull String, float);
+    method public void setFloatUniform(@NonNull String, float, float);
+    method public void setFloatUniform(@NonNull String, float, float, float);
+    method public void setFloatUniform(@NonNull String, float, float, float, float);
+    method public void setFloatUniform(@NonNull String, @NonNull float[]);
+    method public void setInputColorFilter(@NonNull String, @NonNull android.graphics.ColorFilter);
+    method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
+    method public void setIntUniform(@NonNull String, int);
+    method public void setIntUniform(@NonNull String, int, int);
+    method public void setIntUniform(@NonNull String, int, int, int);
+    method public void setIntUniform(@NonNull String, int, int, int, int);
+    method public void setIntUniform(@NonNull String, @NonNull int[]);
+  }
+
   public class RuntimeShader extends android.graphics.Shader {
     ctor public RuntimeShader(@NonNull String);
     method public void setColorUniform(@NonNull String, @ColorInt int);
@@ -19060,11 +19136,11 @@
     method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public void authenticate(@NonNull android.hardware.biometrics.BiometricPrompt.CryptoObject, @NonNull android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
     method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public void authenticate(@NonNull android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
     method @Nullable public int getAllowedAuthenticators();
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable public android.hardware.biometrics.PromptContentView getContentView();
+    method @Nullable public android.hardware.biometrics.PromptContentView getContentView();
     method @Nullable public CharSequence getDescription();
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.graphics.Bitmap getLogoBitmap();
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getLogoDescription();
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public int getLogoRes();
+    method @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.graphics.Bitmap getLogoBitmap();
+    method @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getLogoDescription();
+    method @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public int getLogoRes();
     method @Nullable public CharSequence getNegativeButtonText();
     method @Nullable public CharSequence getSubtitle();
     method @NonNull public CharSequence getTitle();
@@ -19113,12 +19189,12 @@
     method @NonNull public android.hardware.biometrics.BiometricPrompt build();
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setAllowedAuthenticators(int);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setConfirmationRequired(boolean);
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setContentView(@NonNull android.hardware.biometrics.PromptContentView);
+    method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setContentView(@NonNull android.hardware.biometrics.PromptContentView);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);
     method @Deprecated @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean);
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoDescription(@NonNull String);
-    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
+    method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoDescription(@NonNull String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setTitle(@NonNull CharSequence);
@@ -19141,27 +19217,27 @@
     method @Nullable public java.security.Signature getSignature();
   }
 
-  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public interface PromptContentItem {
+  public interface PromptContentItem {
   }
 
-  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentItemBulletedText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
+  public final class PromptContentItemBulletedText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
     ctor public PromptContentItemBulletedText(@NonNull String);
     method public int describeContents();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentItemBulletedText> CREATOR;
   }
 
-  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentItemPlainText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
+  public final class PromptContentItemPlainText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
     ctor public PromptContentItemPlainText(@NonNull String);
     method public int describeContents();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentItemPlainText> CREATOR;
   }
 
-  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public interface PromptContentView {
+  public interface PromptContentView {
   }
 
-  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentViewWithMoreOptionsButton implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
+  public final class PromptContentViewWithMoreOptionsButton implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
     method public int describeContents();
     method @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getDescription();
     method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.content.DialogInterface.OnClickListener getMoreOptionsButtonListener();
@@ -19176,7 +19252,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.Builder setMoreOptionsButtonListener(@NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
   }
 
-  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptVerticalListContentView implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
+  public final class PromptVerticalListContentView implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
     method public int describeContents();
     method @Nullable public String getDescription();
     method @NonNull public java.util.List<android.hardware.biometrics.PromptContentItem> getListItems();
@@ -19271,6 +19347,8 @@
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> AUTOMOTIVE_LENS_FACING;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> AUTOMOTIVE_LOCATION;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
+    field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_MODES;
+    field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES;
     field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
@@ -19572,6 +19650,7 @@
     field public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; // 0x1
     field public static final int COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int COLOR_CORRECTION_ABERRATION_MODE_OFF = 0; // 0x0
+    field @FlaggedApi("com.android.internal.camera.flags.color_temperature") public static final int COLOR_CORRECTION_MODE_CCT = 3; // 0x3
     field public static final int COLOR_CORRECTION_MODE_FAST = 1; // 0x1
     field public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; // 0x0
@@ -19858,6 +19937,8 @@
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> BLACK_LEVEL_LOCK;
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_ABERRATION_MODE;
+    field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_COLOR_TEMPERATURE;
+    field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_COLOR_TINT;
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS;
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_MODE;
     field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM;
@@ -19948,6 +20029,8 @@
     method public int getSequenceId();
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> BLACK_LEVEL_LOCK;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> COLOR_CORRECTION_ABERRATION_MODE;
+    field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> COLOR_CORRECTION_COLOR_TEMPERATURE;
+    field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> COLOR_CORRECTION_COLOR_TINT;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.RggbChannelVector> COLOR_CORRECTION_GAINS;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> COLOR_CORRECTION_MODE;
     field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.ColorSpaceTransform> COLOR_CORRECTION_TRANSFORM;
@@ -20066,6 +20149,7 @@
 
   public class MultiResolutionImageReader implements java.lang.AutoCloseable {
     ctor public MultiResolutionImageReader(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int);
+    ctor @FlaggedApi("com.android.internal.camera.flags.multiresolution_imagereader_usage_public") public MultiResolutionImageReader(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int, long);
     method public void close();
     method protected void finalize();
     method public void flush();
@@ -21328,6 +21412,7 @@
     field public static final int TYPE_IP = 20; // 0x14
     field public static final int TYPE_LINE_ANALOG = 5; // 0x5
     field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
+    field @FlaggedApi("android.media.audio.enable_multichannel_group_device") public static final int TYPE_MULTICHANNEL_GROUP = 32; // 0x20
     field public static final int TYPE_REMOTE_SUBMIX = 25; // 0x19
     field public static final int TYPE_TELEPHONY = 18; // 0x12
     field public static final int TYPE_TV_TUNER = 17; // 0x11
@@ -22625,7 +22710,6 @@
     method public void sendEvent(int, int, @Nullable byte[]) throws android.media.MediaCasException;
     method public void setEventListener(@Nullable android.media.MediaCas.EventListener, @Nullable android.os.Handler);
     method public void setPrivateData(@NonNull byte[]) throws android.media.MediaCasException;
-    method @FlaggedApi("com.android.media.flags.update_client_profile_priority") public boolean updateResourcePriority(int, int);
     field public static final int PLUGIN_STATUS_PHYSICAL_MODULE_CHANGED = 0; // 0x0
     field public static final int PLUGIN_STATUS_SESSION_NUMBER_CHANGED = 1; // 0x1
     field public static final int SCRAMBLING_MODE_AES128 = 9; // 0x9
@@ -23046,6 +23130,65 @@
     field public static final int AC4Profile11 = 514; // 0x202
     field public static final int AC4Profile21 = 1026; // 0x402
     field public static final int AC4Profile22 = 1028; // 0x404
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band0 = 513; // 0x201
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band1 = 514; // 0x202
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band2 = 516; // 0x204
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band3 = 520; // 0x208
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band0 = 257; // 0x101
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band1 = 258; // 0x102
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band2 = 260; // 0x104
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band3 = 264; // 0x108
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band0 = 2049; // 0x801
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band1 = 2050; // 0x802
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band2 = 2052; // 0x804
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band3 = 2056; // 0x808
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band0 = 1025; // 0x401
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band1 = 1026; // 0x402
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band2 = 1028; // 0x404
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band3 = 1032; // 0x408
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band0 = 8193; // 0x2001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band1 = 8194; // 0x2002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band2 = 8196; // 0x2004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band3 = 8200; // 0x2008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band0 = 4097; // 0x1001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band1 = 4098; // 0x1002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band2 = 4100; // 0x1004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band3 = 4104; // 0x1008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band0 = 32769; // 0x8001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band1 = 32770; // 0x8002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band2 = 32772; // 0x8004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band3 = 32776; // 0x8008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band0 = 16385; // 0x4001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band1 = 16386; // 0x4002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band2 = 16388; // 0x4004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band3 = 16392; // 0x4008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band0 = 131073; // 0x20001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band1 = 131074; // 0x20002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band2 = 131076; // 0x20004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band3 = 131080; // 0x20008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band0 = 65537; // 0x10001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band1 = 65538; // 0x10002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band2 = 65540; // 0x10004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band3 = 65544; // 0x10008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band0 = 524289; // 0x80001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band1 = 524290; // 0x80002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band2 = 524292; // 0x80004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band3 = 524296; // 0x80008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band0 = 262145; // 0x40001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band1 = 262146; // 0x40002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band2 = 262148; // 0x40004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band3 = 262152; // 0x40008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band0 = 2097153; // 0x200001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band1 = 2097154; // 0x200002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band2 = 2097156; // 0x200004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band3 = 2097160; // 0x200008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band0 = 1048577; // 0x100001
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band1 = 1048578; // 0x100002
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band2 = 1048580; // 0x100004
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band3 = 1048584; // 0x100008
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10 = 1; // 0x1
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10HDR10 = 4096; // 0x1000
+    field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10HDR10Plus = 8192; // 0x2000
     field public static final int AV1Level2 = 1; // 0x1
     field public static final int AV1Level21 = 2; // 0x2
     field public static final int AV1Level22 = 4; // 0x4
@@ -23896,6 +24039,7 @@
     field public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
     field public static final String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
     field public static final String MIMETYPE_TEXT_VTT = "text/vtt";
+    field @FlaggedApi("android.media.codec.apv_support") public static final String MIMETYPE_VIDEO_APV = "video/apv";
     field public static final String MIMETYPE_VIDEO_AV1 = "video/av01";
     field public static final String MIMETYPE_VIDEO_AVC = "video/avc";
     field public static final String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision";
@@ -24525,6 +24669,7 @@
     field @FlaggedApi("com.android.media.flags.enable_audio_policies_device_and_bluetooth_controller") public static final int TYPE_HDMI_ARC = 10; // 0xa
     field @FlaggedApi("com.android.media.flags.enable_audio_policies_device_and_bluetooth_controller") public static final int TYPE_HDMI_EARC = 29; // 0x1d
     field public static final int TYPE_HEARING_AID = 23; // 0x17
+    field @FlaggedApi("android.media.audio.enable_multichannel_group_device") public static final int TYPE_MULTICHANNEL_SPEAKER_GROUP = 32; // 0x20
     field public static final int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 1003; // 0x3eb
     field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_CAR = 1008; // 0x3f0
     field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_COMPUTER = 1006; // 0x3ee
@@ -34012,9 +34157,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);
@@ -34131,6 +34281,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_ro") @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();
@@ -34143,6 +34294,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_ro") @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();
@@ -34378,11 +34530,14 @@
   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();
+    method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope(@FloatRange(from=0) float);
     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
@@ -37147,7 +37302,7 @@
   }
 
   public static final class ContactsContract.Settings implements android.provider.ContactsContract.SettingsColumns {
-    method @Nullable public static android.accounts.Account getDefaultAccount(@NonNull android.content.ContentResolver);
+    method @Deprecated @FlaggedApi("android.provider.new_default_account_api_enabled") @Nullable public static android.accounts.Account getDefaultAccount(@NonNull android.content.ContentResolver);
     field public static final String ACTION_SET_DEFAULT_ACCOUNT = "android.provider.action.SET_DEFAULT_ACCOUNT";
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/setting";
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/setting";
@@ -39763,7 +39918,7 @@
 
 package android.security.advancedprotection {
 
-  @FlaggedApi("android.security.aapm_api") public class AdvancedProtectionManager {
+  @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager {
     method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public boolean isAdvancedProtectionEnabled();
     method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void registerAdvancedProtectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.security.advancedprotection.AdvancedProtectionManager.Callback);
     method @RequiresPermission(android.Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE) public void unregisterAdvancedProtectionCallback(@NonNull android.security.advancedprotection.AdvancedProtectionManager.Callback);
@@ -40753,7 +40908,7 @@
 
 package android.service.chooser {
 
-  @FlaggedApi("android.service.chooser.chooser_payload_toggling") public interface AdditionalContentContract {
+  public interface AdditionalContentContract {
   }
 
   public static interface AdditionalContentContract.Columns {
@@ -41429,6 +41584,7 @@
     method public final void cancelNotification(String);
     method public final void cancelNotifications(String[]);
     method public final void clearRequestedListenerHints();
+    method @FlaggedApi("android.service.notification.notification_conversation_channel_management") @Nullable public final android.app.NotificationChannel createConversationNotificationChannelForPackage(@NonNull String, @NonNull android.os.UserHandle, @NonNull String, @NonNull String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.service.notification.StatusBarNotification[] getActiveNotifications(String[]);
     method public final int getCurrentInterruptionFilter();
@@ -42033,6 +42189,7 @@
     ctor public WallpaperService();
     method public final android.os.IBinder onBind(android.content.Intent);
     method @MainThread public abstract android.service.wallpaper.WallpaperService.Engine onCreateEngine();
+    method @FlaggedApi("android.app.live_wallpaper_content_handling") @MainThread @Nullable public android.service.wallpaper.WallpaperService.Engine onCreateEngine(@NonNull android.app.wallpaper.WallpaperDescription);
     field public static final String SERVICE_INTERFACE = "android.service.wallpaper.WallpaperService";
     field public static final String SERVICE_META_DATA = "android.service.wallpaper";
   }
@@ -42048,6 +42205,7 @@
     method public boolean isPreview();
     method public boolean isVisible();
     method public void notifyColorsChanged();
+    method @FlaggedApi("android.app.live_wallpaper_content_handling") @Nullable public android.app.wallpaper.WallpaperDescription onApplyWallpaper(int);
     method @MainThread public void onApplyWindowInsets(android.view.WindowInsets);
     method @MainThread public android.os.Bundle onCommand(String, int, int, int, android.os.Bundle, boolean);
     method @MainThread @Nullable public android.app.WallpaperColors onComputeColors();
@@ -47718,6 +47876,19 @@
 
 }
 
+package android.telephony.satellite {
+
+  @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public final class SatelliteManager {
+    method @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") @RequiresPermission(anyOf={android.Manifest.permission.READ_BASIC_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public void registerStateChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateChangeListener);
+    method @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") @RequiresPermission(anyOf={android.Manifest.permission.READ_BASIC_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE, "carrier privileges"}) public void unregisterStateChangeListener(@NonNull android.telephony.satellite.SatelliteStateChangeListener);
+  }
+
+  @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public interface SatelliteStateChangeListener {
+    method public void onEnabledStateChanged(boolean);
+  }
+
+}
+
 package android.text {
 
   @Deprecated public class AlteredCharSequence implements java.lang.CharSequence android.text.GetChars {
@@ -51028,6 +51199,7 @@
     method @NonNull public android.view.DisplayShape getShape();
     method @Deprecated public void getSize(android.graphics.Point);
     method public int getState();
+    method @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public float getSuggestedFrameRate(int);
     method public android.view.Display.Mode[] getSupportedModes();
     method @Deprecated public float[] getSupportedRefreshRates();
     method @Deprecated public int getWidth();
@@ -51045,6 +51217,8 @@
     field public static final int FLAG_ROUND = 16; // 0x10
     field public static final int FLAG_SECURE = 2; // 0x2
     field public static final int FLAG_SUPPORTS_PROTECTED_BUFFERS = 1; // 0x1
+    field @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public static final int FRAME_RATE_CATEGORY_HIGH = 1; // 0x1
+    field @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public static final int FRAME_RATE_CATEGORY_NORMAL = 0; // 0x0
     field public static final int INVALID_DISPLAY = -1; // 0xffffffff
     field public static final int STATE_DOZE = 3; // 0x3
     field public static final int STATE_DOZE_SUSPEND = 4; // 0x4
@@ -52604,6 +52778,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);
@@ -53072,6 +53247,7 @@
     method public android.animation.StateListAnimator getStateListAnimator();
     method protected int getSuggestedMinimumHeight();
     method protected int getSuggestedMinimumWidth();
+    method @FlaggedApi("android.view.accessibility.supplemental_description") @Nullable public CharSequence getSupplementalDescription();
     method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects();
     method @Deprecated public int getSystemUiVisibility();
     method public Object getTag();
@@ -53452,6 +53628,7 @@
     method public void setSoundEffectsEnabled(boolean);
     method public void setStateDescription(@Nullable CharSequence);
     method public void setStateListAnimator(android.animation.StateListAnimator);
+    method @FlaggedApi("android.view.accessibility.supplemental_description") public void setSupplementalDescription(@Nullable CharSequence);
     method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>);
     method @Deprecated public void setSystemUiVisibility(int);
     method public void setTag(Object);
@@ -54006,6 +54183,7 @@
     method public void onStopNestedScroll(android.view.View);
     method public void onViewAdded(android.view.View);
     method public void onViewRemoved(android.view.View);
+    method @FlaggedApi("android.view.flags.toolkit_viewgroup_set_requested_frame_rate_api") public void propagateRequestedFrameRate(float, boolean);
     method public void recomputeViewAttributes(android.view.View);
     method public void removeAllViews();
     method public void removeAllViewsInLayout();
@@ -55058,6 +55236,7 @@
     field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
     field public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 64; // 0x40
     field public static final int CONTENT_CHANGE_TYPE_SUBTREE = 1; // 0x1
+    field @FlaggedApi("android.view.accessibility.supplemental_description") public static final int CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION = 32768; // 0x8000
     field public static final int CONTENT_CHANGE_TYPE_TEXT = 2; // 0x2
     field public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0; // 0x0
     field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityEvent> CREATOR;
@@ -55220,6 +55399,7 @@
     method @Nullable public android.view.accessibility.AccessibilityNodeInfo getParent(int);
     method public android.view.accessibility.AccessibilityNodeInfo.RangeInfo getRangeInfo();
     method @Nullable public CharSequence getStateDescription();
+    method @FlaggedApi("android.view.accessibility.supplemental_description") @Nullable public CharSequence getSupplementalDescription();
     method public CharSequence getText();
     method public int getTextSelectionEnd();
     method public int getTextSelectionStart();
@@ -55242,6 +55422,7 @@
     method public boolean isDismissable();
     method public boolean isEditable();
     method public boolean isEnabled();
+    method @FlaggedApi("android.view.accessibility.a11y_is_required_api") public boolean isFieldRequired();
     method public boolean isFocusable();
     method public boolean isFocused();
     method @FlaggedApi("android.view.accessibility.granular_scrolling") public boolean isGranularScrollingSupported();
@@ -55296,6 +55477,7 @@
     method public void setEnabled(boolean);
     method public void setError(CharSequence);
     method @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public void setExpandedState(int);
+    method @FlaggedApi("android.view.accessibility.a11y_is_required_api") public void setFieldRequired(boolean);
     method public void setFocusable(boolean);
     method public void setFocused(boolean);
     method @FlaggedApi("android.view.accessibility.granular_scrolling") public void setGranularScrollingSupported(boolean);
@@ -55328,6 +55510,7 @@
     method public void setSource(android.view.View);
     method public void setSource(android.view.View, int);
     method public void setStateDescription(@Nullable CharSequence);
+    method @FlaggedApi("android.view.accessibility.supplemental_description") public void setSupplementalDescription(@Nullable CharSequence);
     method public void setText(CharSequence);
     method public void setTextEntryKey(boolean);
     method public void setTextSelectable(boolean);
@@ -55530,6 +55713,7 @@
     method public int getType();
     method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.RangeInfo obtain(int, float, float, float);
     field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
+    field @FlaggedApi("android.view.accessibility.indeterminate_range_info") public static final int RANGE_TYPE_INDETERMINATE = 3; // 0x3
     field public static final int RANGE_TYPE_INT = 0; // 0x0
     field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
   }
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/module-lib-current.txt b/core/api/module-lib-current.txt
index 4d1a423..bc73220 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -102,6 +102,7 @@
     method @NonNull public android.os.UserHandle getUser();
     field public static final String PAC_PROXY_SERVICE = "pac_proxy";
     field public static final String TEST_NETWORK_SERVICE = "test_network";
+    field @FlaggedApi("android.os.mainline_vcn_platform_api") public static final String VCN_MANAGEMENT_SERVICE = "vcn_management";
     field @FlaggedApi("android.webkit.update_service_ipc_wrapper") public static final String WEBVIEW_UPDATE_SERVICE = "webviewupdate";
   }
 
@@ -144,21 +145,12 @@
 
   public class UsbManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int getGadgetHalVersion();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int getUsbBandwidthMbps();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int getUsbHalVersion();
     field public static final int GADGET_HAL_NOT_SUPPORTED = -1; // 0xffffffff
     field public static final int GADGET_HAL_V1_0 = 10; // 0xa
     field public static final int GADGET_HAL_V1_1 = 11; // 0xb
     field public static final int GADGET_HAL_V1_2 = 12; // 0xc
     field public static final int GADGET_HAL_V2_0 = 20; // 0x14
-    field public static final int USB_DATA_TRANSFER_RATE_10G = 10240; // 0x2800
-    field public static final int USB_DATA_TRANSFER_RATE_20G = 20480; // 0x5000
-    field public static final int USB_DATA_TRANSFER_RATE_40G = 40960; // 0xa000
-    field public static final int USB_DATA_TRANSFER_RATE_5G = 5120; // 0x1400
-    field public static final int USB_DATA_TRANSFER_RATE_FULL_SPEED = 12; // 0xc
-    field public static final int USB_DATA_TRANSFER_RATE_HIGH_SPEED = 480; // 0x1e0
-    field public static final int USB_DATA_TRANSFER_RATE_LOW_SPEED = 2; // 0x2
-    field public static final int USB_DATA_TRANSFER_RATE_UNKNOWN = -1; // 0xffffffff
     field public static final int USB_HAL_NOT_SUPPORTED = -1; // 0xffffffff
     field public static final int USB_HAL_RETRY = -2; // 0xfffffffe
     field public static final int USB_HAL_V1_0 = 10; // 0xa
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d1a9e67..011b1ea 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -382,6 +382,7 @@
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
     field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
+    field @FlaggedApi("android.media.tv.flags.kids_mode_tvdb_sharing") public static final String SINGLE_USER_TIS_ACCESS = "android.permission.SINGLE_USER_TIS_ACCESS";
     field public static final String SOUND_TRIGGER_RUN_IN_BATTERY_SAVER = "android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER";
     field public static final String STAGE_HEALTH_CONNECT_REMOTE_DATA = "android.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA";
     field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
@@ -703,6 +704,7 @@
     field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
     field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video";
     field public static final String OPSTR_READ_MEDIA_VISUAL_USER_SELECTED = "android:read_media_visual_user_selected";
+    field @FlaggedApi("android.permission.flags.platform_skin_temperature_enabled") public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature";
     field public static final String OPSTR_READ_WRITE_HEALTH_DATA = "android:read_write_health_data";
     field public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO = "android:receive_ambient_trigger_audio";
     field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
@@ -1047,6 +1049,7 @@
     method public int getUserLockedFields();
     method public boolean isDeleted();
     method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
+    method @FlaggedApi("android.service.notification.notification_conversation_channel_management") public void setImportantConversation(boolean);
     method public org.json.JSONObject toJson() throws org.json.JSONException;
     method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
     field public static final int USER_LOCKED_SOUND = 32; // 0x20
@@ -1264,8 +1267,10 @@
   public class WallpaperManager {
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int);
     method @FloatRange(from=0.0f, to=1.0f) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount();
+    method @FlaggedApi("android.app.live_wallpaper_content_handling") @Nullable @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.app.wallpaper.WallpaperInstance getWallpaperInstance(int);
     method public void setDisplayOffset(android.os.IBinder, int, int);
     method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName);
+    method @FlaggedApi("android.app.live_wallpaper_content_handling") @RequiresPermission(allOf={android.Manifest.permission.SET_WALLPAPER_COMPONENT, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public boolean setWallpaperComponentWithDescription(@NonNull android.app.wallpaper.WallpaperDescription, int);
     method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponentWithFlags(@NonNull android.content.ComponentName, int);
     method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange(from=0.0f, to=1.0f) float);
   }
@@ -3355,7 +3360,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();
@@ -3367,29 +3379,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
   }
 
@@ -5273,11 +5291,13 @@
   public final class VirtualDisplayConfig implements android.os.Parcelable {
     method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @Nullable public android.view.DisplayCutout getDisplayCutout();
     method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
+    method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") public boolean isIgnoreActivitySizeRestrictions();
   }
 
   public static final class VirtualDisplayConfig.Builder {
     method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
     method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
+    method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setIgnoreActivitySizeRestrictions(boolean);
   }
 
 }
@@ -7053,6 +7073,7 @@
   public class UsbManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public long getCurrentFunctions();
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USB) public java.util.List<android.hardware.usb.UsbPort> getPorts();
+    method @FlaggedApi("android.hardware.usb.flags.expose_usb_speed_system_api") @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int getUsbBandwidthMbps();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
     method public static boolean isUvcSupportEnabled();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void registerDisplayPortAltModeInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.usb.UsbManager.DisplayPortAltModeInfoListener);
@@ -7079,6 +7100,14 @@
     field public static final long FUNCTION_UVC = 128L; // 0x80L
     field public static final String USB_CONFIGURED = "configured";
     field public static final String USB_CONNECTED = "connected";
+    field @FlaggedApi("android.hardware.usb.flags.expose_usb_speed_system_api") public static final int USB_DATA_TRANSFER_RATE_10G = 10240; // 0x2800
+    field @FlaggedApi("android.hardware.usb.flags.expose_usb_speed_system_api") public static final int USB_DATA_TRANSFER_RATE_20G = 20480; // 0x5000
+    field @FlaggedApi("android.hardware.usb.flags.expose_usb_speed_system_api") public static final int USB_DATA_TRANSFER_RATE_40G = 40960; // 0xa000
+    field @FlaggedApi("android.hardware.usb.flags.expose_usb_speed_system_api") public static final int USB_DATA_TRANSFER_RATE_5G = 5120; // 0x1400
+    field @FlaggedApi("android.hardware.usb.flags.expose_usb_speed_system_api") public static final int USB_DATA_TRANSFER_RATE_FULL_SPEED = 12; // 0xc
+    field @FlaggedApi("android.hardware.usb.flags.expose_usb_speed_system_api") public static final int USB_DATA_TRANSFER_RATE_HIGH_SPEED = 480; // 0x1e0
+    field @FlaggedApi("android.hardware.usb.flags.expose_usb_speed_system_api") public static final int USB_DATA_TRANSFER_RATE_LOW_SPEED = 2; // 0x2
+    field @FlaggedApi("android.hardware.usb.flags.expose_usb_speed_system_api") public static final int USB_DATA_TRANSFER_RATE_UNKNOWN = -1; // 0xffffffff
     field public static final String USB_FUNCTION_NCM = "ncm";
     field public static final String USB_FUNCTION_RNDIS = "rndis";
   }
@@ -7362,7 +7391,7 @@
     field public static final String EXTRA_VOLUME_STREAM_VALUE = "android.media.EXTRA_VOLUME_STREAM_VALUE";
     field public static final int FLAG_BLUETOOTH_ABS_VOLUME = 64; // 0x40
     field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int STREAM_ASSISTANT = 11; // 0xb
-    field public static final int STREAM_BLUETOOTH_SCO = 6; // 0x6
+    field @Deprecated @FlaggedApi("android.media.audio.deprecate_stream_bt_sco") public static final int STREAM_BLUETOOTH_SCO = 6; // 0x6
     field public static final int SUCCESS = 0; // 0x0
   }
 
@@ -7559,6 +7588,11 @@
     method @NonNull public android.media.HwAudioSource.Builder setAudioDeviceInfo(@NonNull android.media.AudioDeviceInfo);
   }
 
+  public final class MediaCas implements java.lang.AutoCloseable {
+    method @FlaggedApi("android.media.tv.flags.set_resource_holder_retain") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void setResourceHolderRetain(boolean);
+    method @FlaggedApi("com.android.media.flags.update_client_profile_priority") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean updateResourcePriority(int, int);
+  }
+
   public final class MediaCodec {
     method @NonNull @RequiresPermission(android.Manifest.permission.MEDIA_RESOURCE_OVERRIDE_PID) public static android.media.MediaCodec createByCodecNameForClient(@NonNull String, int, int) throws java.io.IOException;
   }
@@ -7574,7 +7608,7 @@
   public final class MediaRecorder.AudioSource {
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int ECHO_REFERENCE = 1997; // 0x7cd
     field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD) public static final int HOTWORD = 1999; // 0x7cf
-    field @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
+    field @RequiresPermission(android.Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT) public static final int RADIO_TUNER = 1998; // 0x7ce
     field @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public static final int ULTRASOUND = 2000; // 0x7d0
   }
 
@@ -8131,6 +8165,7 @@
     method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPid(@NonNull String);
     method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPriority(int, @NonNull String);
     method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public int getClientPriority(int);
+    method @FlaggedApi("android.media.tv.flags.kids_mode_tvdb_sharing") @RequiresPermission(android.Manifest.permission.SINGLE_USER_TIS_ACCESS) public int getClientUserId(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos();
     method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
     method @Nullable @RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE) public android.os.IBinder getExtensionInterface(@NonNull String, @NonNull String);
@@ -8331,6 +8366,7 @@
     method public int setLnaEnabled(boolean);
     method public int setMaxNumberOfFrontends(int, @IntRange(from=0) int);
     method public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
+    method @FlaggedApi("android.media.tv.flags.set_resource_holder_retain") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void setResourceHolderRetain(boolean);
     method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
     method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
     method public int transferOwner(@NonNull android.media.tv.tuner.Tuner);
@@ -10978,7 +11014,7 @@
   public class Environment {
     method @NonNull public static java.io.File getDataCePackageDirectoryForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle, @NonNull String);
     method @NonNull public static java.io.File getDataDePackageDirectoryForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle, @NonNull String);
-    method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") @NonNull public static java.io.File getDataSystemDeDirectory();
+    method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") @NonNull public static java.io.File getDataSystemDeviceProtectedDirectory();
     method @NonNull public static java.util.Collection<java.io.File> getInternalMediaDirectories();
     method @NonNull public static java.io.File getOdmDirectory();
     method @NonNull public static java.io.File getOemDirectory();
@@ -12078,7 +12114,7 @@
   }
 
   public static final class ContactsContract.Settings implements android.provider.ContactsContract.SettingsColumns {
-    method @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS) public static void setDefaultAccount(@NonNull android.content.ContentResolver, @Nullable android.accounts.Account);
+    method @Deprecated @FlaggedApi("android.provider.new_default_account_api_enabled") @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS) public static void setDefaultAccount(@NonNull android.content.ContentResolver, @Nullable android.accounts.Account);
   }
 
   public static final class ContactsContract.SimContacts {
@@ -12435,7 +12471,16 @@
 
 package android.security.advancedprotection {
 
-  @FlaggedApi("android.security.aapm_api") public class AdvancedProtectionManager {
+  @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionFeature implements android.os.Parcelable {
+    ctor public AdvancedProtectionFeature(@NonNull String);
+    method public int describeContents();
+    method @NonNull public String getId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.security.advancedprotection.AdvancedProtectionFeature> CREATOR;
+  }
+
+  @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public java.util.List<android.security.advancedprotection.AdvancedProtectionFeature> getAdvancedProtectionFeatures();
     method @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean);
   }
 
@@ -14086,34 +14131,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
@@ -14126,11 +14146,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);
@@ -15696,6 +15718,9 @@
     method @FlaggedApi("android.permission.flags.get_emergency_role_holder_api_enabled") @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getEmergencyAssistancePackageName();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
+    method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @NonNull @RequiresPermission(value=android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional=true) public java.util.List<java.lang.String> getImsPcscfAddresses();
+    method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @Nullable @RequiresPermission(android.Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER) public String getImsPrivateUserIdentity();
+    method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @NonNull @RequiresPermission(value=android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional=true) public java.util.List<android.net.Uri> getImsPublicUserIdentities();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
     method @FlaggedApi("com.android.server.telecom.flags.get_last_known_cell_identity") @Nullable @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_LAST_KNOWN_CELL_ID}) public android.telephony.CellIdentity getLastKnownCellIdentity();
@@ -15714,6 +15739,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int, int);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
+    method @FlaggedApi("com.android.internal.telephony.flags.support_isim_record") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getSimServiceTable(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<byte[],java.lang.Exception>);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Collection<android.telephony.UiccSlotMapping> getSimSlotMapping();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telephony.RadioAccessSpecifier> getSystemSelectionChannels();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
@@ -16838,8 +16864,11 @@
     method public void callSessionRttMessageReceived(String);
     method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionRttModifyResponseReceived(int);
+    method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void callSessionSendAnbrQuery(int, int, @IntRange(from=0) int);
     method public void callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification);
     method public void callSessionTerminated(android.telephony.ims.ImsReasonInfo);
+    method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public void callSessionTransferFailed(@NonNull android.telephony.ims.ImsReasonInfo);
+    method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public void callSessionTransferred();
     method public void callSessionTtyModeReceived(int);
     method public void callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
@@ -17644,6 +17673,26 @@
     method public int getRadioTech();
   }
 
+  @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final class ConnectionFailureInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCauseCode();
+    method public int getReason();
+    method public int getWaitTimeMillis();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.feature.ConnectionFailureInfo> CREATOR;
+    field public static final int REASON_ACCESS_DENIED = 1; // 0x1
+    field public static final int REASON_NAS_FAILURE = 2; // 0x2
+    field public static final int REASON_NONE = 0; // 0x0
+    field public static final int REASON_NO_SERVICE = 7; // 0x7
+    field public static final int REASON_PDN_NOT_AVAILABLE = 8; // 0x8
+    field public static final int REASON_RACH_FAILURE = 3; // 0x3
+    field public static final int REASON_RF_BUSY = 9; // 0x9
+    field public static final int REASON_RLC_FAILURE = 4; // 0x4
+    field public static final int REASON_RRC_REJECT = 5; // 0x5
+    field public static final int REASON_RRC_TIMEOUT = 6; // 0x6
+    field public static final int REASON_UNSPECIFIED = 65535; // 0xffff
+  }
+
   public abstract class ImsFeature {
     ctor public ImsFeature();
     method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
@@ -17670,6 +17719,11 @@
     method public void onChangeCapabilityConfigurationError(int, int, int);
   }
 
+  @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public interface ImsTrafficSessionCallback {
+    method public void onError(@NonNull android.telephony.ims.feature.ConnectionFailureInfo);
+    method public void onReady();
+  }
+
   public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
     ctor public MmTelFeature();
     ctor public MmTelFeature(@NonNull java.util.concurrent.Executor);
@@ -17682,6 +17736,7 @@
     method @NonNull public android.telephony.ims.stub.ImsMultiEndpointImplBase getMultiEndpoint();
     method @NonNull public android.telephony.ims.stub.ImsSmsImplBase getSmsImplementation();
     method @NonNull public android.telephony.ims.stub.ImsUtImplBase getUt();
+    method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void modifyImsTrafficSession(int, @NonNull android.telephony.ims.feature.ImsTrafficSessionCallback);
     method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
     method @Deprecated public final void notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull android.os.Bundle);
     method @Nullable public final android.telephony.ims.ImsCallSessionListener notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull String, @NonNull android.os.Bundle);
@@ -17702,10 +17757,24 @@
     method public void setTerminalBasedCallWaitingStatus(boolean);
     method public void setUiTtyMode(int, @Nullable android.os.Message);
     method public int shouldProcessCall(@NonNull String[]);
+    method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void startImsTrafficSession(int, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.feature.ImsTrafficSessionCallback);
+    method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void stopImsTrafficSession(@NonNull android.telephony.ims.feature.ImsTrafficSessionCallback);
+    method @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public final void triggerEpsFallback(int);
     field public static final int AUDIO_HANDLER_ANDROID = 0; // 0x0
     field public static final int AUDIO_HANDLER_BASEBAND = 1; // 0x1
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE = 2; // 0x2
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER = 1; // 0x1
     field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
     field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_DIRECTION_INCOMING = 0; // 0x0
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_DIRECTION_OUTGOING = 1; // 0x1
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_EMERGENCY = 0; // 0x0
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_EMERGENCY_SMS = 1; // 0x1
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_REGISTRATION = 5; // 0x5
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_SMS = 4; // 0x4
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_UT_XCAP = 6; // 0x6
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_VIDEO = 3; // 0x3
+    field @FlaggedApi("com.android.internal.telephony.flags.support_ims_mmtel_interface") public static final int IMS_TRAFFIC_TYPE_VOICE = 2; // 0x2
     field public static final int PROCESS_CALL_CSFB = 1; // 0x1
     field public static final int PROCESS_CALL_IMS = 0; // 0x0
   }
@@ -18189,7 +18258,7 @@
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
   }
 
-  @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteManager {
+  @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public final class SatelliteManager {
     method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getAttachRestrictionReasonsForCarrier(int);
@@ -19011,10 +19080,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 58ab073..98d6f58 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -369,6 +369,7 @@
   }
 
   public final class NotificationChannel implements android.os.Parcelable {
+    method @FlaggedApi("android.service.notification.notification_conversation_channel_management") @NonNull public android.app.NotificationChannel copy();
     method public int getOriginalImportance();
     method public boolean isImportanceLockedByCriticalDeviceFunction();
     method public void lockFields(int);
@@ -376,7 +377,7 @@
     method public void setDeletedTimeMs(long);
     method public void setDemoted(boolean);
     method public void setImportanceLockedByCriticalDeviceFunction(boolean);
-    method public void setImportantConversation(boolean);
+    method @FlaggedApi("android.service.notification.notification_conversation_channel_management") public void setImportantConversation(boolean);
     method public void setOriginalImportance(int);
     method public void setUserVisibleTaskShown(boolean);
     field @FlaggedApi("android.service.notification.notification_classification") public static final String NEWS_ID = "android.app.news";
@@ -1029,6 +1030,7 @@
   public abstract class Context {
     method @NonNull public java.io.File getCrateDir(@NonNull String);
     method public abstract int getDisplayId();
+    method @NonNull public java.util.List<android.content.IntentFilter> getRegisteredIntentFilters(@NonNull android.content.BroadcastReceiver);
     method @NonNull public android.os.UserHandle getUser();
     method public int getUserId();
     method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
@@ -3301,14 +3303,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/Activity.java b/core/java/android/app/Activity.java
index 4fb35c3..7a1c759 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1262,6 +1262,33 @@
         }
     }
 
+    /**
+     * To make users aware of system features such as the app header menu and its various
+     * functionalities, educational dialogs are shown to demonstrate how to find and utilize these
+     * features. Using this method, an activity can specify if it wants these educational dialogs to
+     * be shown. When set to {@code true}, these dialogs are not completely blocked; however, the
+     * system will be notified that they should not be shown unless necessary. If this API is not
+     * called, the system's educational dialogs are not limited by default.
+     *
+     * <p>This method can be utilized when activities have states where showing an
+     * educational dialog would be disruptive to the user. For example, if a game application is
+     * expecting prompt user input, this method can be used to limit educational dialogs such as the
+     * dialogs that showcase the app header's features which, in this instance, would disrupt the
+     * user's experience if shown.</p>
+     *
+     * <p>Note that educational dialogs may be shown soon after this activity is launched, so
+     * this method must be called early if the intent is to limit the dialogs from the start.</p>
+     */
+    @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
+    public final void setLimitSystemEducationDialogs(boolean limitSystemEducationDialogs) {
+        try {
+            ActivityTaskManager
+                  .getService().setLimitSystemEducationDialogs(mToken, limitSystemEducationDialogs);
+        } catch (RemoteException e) {
+            // Empty
+        }
+    }
+
     /** Return the application that owns this activity. */
     public final Application getApplication() {
         return mApplication;
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 56538d9..2b0e86c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1618,9 +1618,12 @@
     /** @hide Access to read heart rate sensor. */
     public static final int OP_READ_HEART_RATE = AppProtoEnums.APP_OP_READ_HEART_RATE;
 
+    /** @hide Access to read skin temperature. */
+    public static final int OP_READ_SKIN_TEMPERATURE = AppProtoEnums.APP_OP_READ_SKIN_TEMPERATURE;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 150;
+    public static final int _NUM_OP = 151;
 
     /**
      * All app ops represented as strings.
@@ -1774,6 +1777,7 @@
             OPSTR_EMERGENCY_LOCATION,
             OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS,
             OPSTR_READ_HEART_RATE,
+            OPSTR_READ_SKIN_TEMPERATURE,
     })
     public @interface AppOpString {}
 
@@ -2516,6 +2520,11 @@
     @FlaggedApi(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED)
     public static final String OPSTR_READ_HEART_RATE = "android:read_heart_rate";
 
+    /** @hide Access to read skin temperature. */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_PLATFORM_SKIN_TEMPERATURE_ENABLED)
+    public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2591,6 +2600,7 @@
             OP_POST_NOTIFICATION,
             // Health
             Flags.replaceBodySensorPermissionEnabled() ? OP_READ_HEART_RATE : OP_NONE,
+            Flags.platformSkinTemperatureEnabled() ? OP_READ_SKIN_TEMPERATURE : OP_NONE,
     };
 
     /**
@@ -3103,6 +3113,11 @@
             .setPermission(Flags.replaceBodySensorPermissionEnabled() ?
                 HealthPermissions.READ_HEART_RATE : null)
             .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_SKIN_TEMPERATURE, OPSTR_READ_SKIN_TEMPERATURE,
+            "READ_SKIN_TEMPERATURE").setPermission(
+                Flags.platformSkinTemperatureEnabled()
+                    ? HealthPermissions.READ_SKIN_TEMPERATURE : null)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
     };
 
     // The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ecef0db..2cf718e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1962,6 +1962,24 @@
         }
     }
 
+    @Override
+    @NonNull
+    public List<IntentFilter> getRegisteredIntentFilters(@NonNull BroadcastReceiver receiver) {
+        if (mPackageInfo != null) {
+            IIntentReceiver rd = mPackageInfo.findRegisteredReceiverDispatcher(
+                    receiver, getOuterContext());
+            try {
+                final List<IntentFilter> filters = ActivityManager.getService()
+                        .getRegisteredIntentFilters(rd);
+                return filters == null ? new ArrayList<>() : filters;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            throw new RuntimeException("Not supported in system context");
+        }
+    }
+
     private void validateServiceIntent(Intent service) {
         if (service.getComponent() == null && service.getPackage() == null) {
             if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f880901..34a3ad1 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -181,6 +181,7 @@
             in IntentFilter filter, in String requiredPermission, int userId, int flags);
     @UnsupportedAppUsage
     void unregisterReceiver(in IIntentReceiver receiver);
+    List<IntentFilter> getRegisteredIntentFilters(in IIntentReceiver receiver);
     /** @deprecated Use {@link #broadcastIntentWithFeature} instead */
     @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#sendBroadcast(android.content.Intent)} instead")
     int broadcastIntent(in IApplicationThread caller, in Intent intent,
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 0a05144..ec7b72e 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -242,6 +242,9 @@
 
     boolean supportsLocalVoiceInteraction();
 
+    // Sets whether system educational dialogs should be limited
+    void setLimitSystemEducationDialogs(IBinder appToken, boolean limitSystemEducationDialogs);
+
     // Get device configuration
     ConfigurationInfo getDeviceConfigurationInfo();
 
@@ -359,6 +362,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/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a7597b4..a97fa18 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -175,6 +175,7 @@
     void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
     void setInterruptionFilter(String pkg, int interruptionFilter, boolean fromUser);
 
+    NotificationChannel createConversationNotificationChannelForPackageFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, String parentChannelId, String conversationId);
     void updateNotificationChannelGroupFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannelGroup group);
     void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannel channel);
     ParceledListSlice getNotificationChannelsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user);
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/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 5acb9b5..f693e9b 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -24,6 +24,8 @@
 import android.app.IWallpaperManagerCallback;
 import android.app.ILocalWallpaperColorConsumer;
 import android.app.WallpaperInfo;
+import android.app.wallpaper.WallpaperDescription;
+import android.app.wallpaper.WallpaperInstance;
 import android.content.ComponentName;
 import android.app.WallpaperColors;
 
@@ -61,8 +63,8 @@
     /**
      * Set the live wallpaper.
      */
-    void setWallpaperComponentChecked(in ComponentName name, in String callingPackage, int which,
-            int userId);
+    void setWallpaperComponentChecked(in WallpaperDescription description, in String callingPackage,
+            int which, int userId);
 
     /**
      * Set the live wallpaper. This only affects the system wallpaper.
@@ -129,6 +131,13 @@
      */
     WallpaperInfo getWallpaperInfoWithFlags(int which, int userId);
 
+   /**
+    * Return the instance information about the wallpaper described by `which`, or null if lock
+    * screen is requested and it is the same as home.
+    */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL)")
+    WallpaperInstance getWallpaperInstance(int which, int userId);
+
     /**
      * Return a file descriptor for the file that contains metadata about the given user's
      * wallpaper.
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 1e45d6f..b8233bc 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1627,6 +1627,18 @@
         }
     }
 
+    IIntentReceiver findRegisteredReceiverDispatcher(BroadcastReceiver r, Context context) {
+        synchronized (mReceivers) {
+            final ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map =
+                    mReceivers.get(context);
+            if (map != null) {
+                final LoadedApk.ReceiverDispatcher rd = map.get(r);
+                return rd == null ? null : rd.getIIntentReceiver();
+            }
+            return null;
+        }
+    }
+
     public IIntentReceiver forgetReceiverDispatcher(Context context,
             BroadcastReceiver r) {
         synchronized (mReceivers) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ca1662e6..c6c0395 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11831,7 +11831,7 @@
                     if (length <= 0) continue;
 
                     try {
-                        totalLength += Math.addExact(totalLength, length);
+                        totalLength = Math.addExact(totalLength, length);
                         segments.add(sanitizeSegment(segment, backgroundColor,
                                 defaultProgressColor));
                     } catch (ArithmeticException e) {
@@ -11853,7 +11853,6 @@
                 for (Point point : mProgressPoints) {
                     final int position = point.getPosition();
                     if (position < 0 || position > totalLength) continue;
-
                     points.add(sanitizePoint(point, backgroundColor, defaultProgressColor));
                 }
 
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index ebe7b3a..73d26b8 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,8 @@
  */
 package android.app;
 
+import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
+
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -475,9 +477,10 @@
         dest.writeBoolean(mImportanceLockedDefaultApp);
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
+    @TestApi
+    @NonNull
+    @FlaggedApi(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
     public NotificationChannel copy() {
         NotificationChannel copy = new NotificationChannel(mId, mName, mImportance);
         copy.setDescription(mDesc);
@@ -548,10 +551,10 @@
         mDeletedTime = time;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @TestApi
+    @SystemApi
+    @FlaggedApi(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
     public void setImportantConversation(boolean importantConvo) {
         mImportantConvo = importantConvo;
     }
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index bc9e709..a39cf84 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -70,6 +70,7 @@
  * @hide
  */
 @TestApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class PropertyInvalidatedCache<Query, Result> {
     /**
      * This is a configuration class that customizes a cache instance.
@@ -798,14 +799,32 @@
             = new ConcurrentHashMap<>();
 
     // True if shared memory is flag-enabled, false otherwise.  Read the flags exactly once.
-    private static final boolean sSharedMemoryAvailable =
-            com.android.internal.os.Flags.applicationSharedMemoryEnabled()
-            && android.app.Flags.picUsesSharedMemory();
+    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)
+    }
+
+    /**
+     * Keys that cannot be put in shared memory yet.
+     */
+    private static boolean inSharedMemoryDenyList(@NonNull String name) {
+        final String pkginfo = PREFIX_SYSTEM + "package_info";
+        return name.equals(pkginfo);
+    };
 
     // 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 sSharedMemoryAvailable
+                && name.startsWith(PREFIX_SYSTEM)
+                && !inSharedMemoryDenyList(name);
     }
 
     /**
@@ -944,7 +963,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) {
@@ -1787,14 +1811,6 @@
         // block.
         private static final int MAX_STRING_LENGTH = 63;
 
-        // The raw byte block.  Strings are stored as run-length encoded byte arrays.  The first
-        // byte is the length of the following string.  It is an axiom of the system that the
-        // string block is initially all zeros and that it is write-once memory: new strings are
-        // appended to existing strings, so there is never a need to revisit strings that have
-        // already been pulled from the string block.
-        @GuardedBy("mLock")
-        private final byte[] mStringBlock;
-
         // 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
@@ -1806,12 +1822,15 @@
         // 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;
-            mStringBlock = new byte[nativeGetMaxByte(ptr)];
+            mMaxByte = nativeGetMaxByte(ptr);
             mMaxNonce = nativeGetMaxNonce(ptr);
             refreshStringBlockLocked();
         }
@@ -1870,17 +1889,17 @@
         // 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() {
+        private void updateStringMapLocked(byte[] block) {
             int index = 0;
             int offset = 0;
-            while (offset < mStringBlock.length && mStringBlock[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(mStringBlock, offset+1, mStringBlock[offset]);
+                    final String s = new String(block, offset+1, block[offset]);
                     mStringHandle.put(s, index);
                     mHighestIndex = index;
                 }
-                offset += mStringBlock[offset] + 1;
+                offset += block[offset] + 1;
                 index++;
             }
             mStringBytes = offset;
@@ -1889,24 +1908,21 @@
         // 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) {
+        private void appendStringToMapLocked(@NonNull String str, @NonNull byte[] block) {
             int offset = 0;
-            while (offset < mStringBlock.length && mStringBlock[offset] != 0) {
-                offset += mStringBlock[offset] + 1;
+            while (offset < block.length && block[offset] != 0) {
+                offset += block[offset] + 1;
             }
             final byte[] strBytes = str.getBytes();
 
-            if (offset + strBytes.length >= mStringBlock.length) {
+            if (offset + strBytes.length >= block.length) {
                 // Overflow.  Do not add the string to the block; the string will remain undefined.
                 return;
             }
 
-            mStringBlock[offset] = (byte) strBytes.length;
-            offset++;
-            for (int i = 0; i < strBytes.length; i++, offset++) {
-                mStringBlock[offset] = strBytes[i];
-            }
-            mBlockHash = Arrays.hashCode(mStringBlock);
+            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,
@@ -1917,8 +1933,9 @@
                 // The fastest way to know that the shared memory string block has not changed.
                 return;
             }
-            final int hash = nativeGetByteBlock(mPtr, mBlockHash, mStringBlock);
-            if (hash != Arrays.hashCode(mStringBlock)) {
+            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
@@ -1930,7 +1947,7 @@
             // The hash has changed.  Update the strings from the byte block.
             mStringUpdated++;
             mBlockHash = hash;
-            updateStringMapLocked();
+            updateStringMapLocked(block);
         }
 
         // Throw an exception if the string cannot be stored in the string block.
@@ -1963,6 +1980,9 @@
             }
         }
 
+        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.
@@ -1972,9 +1992,11 @@
                 if (handle == null) {
                     throwIfImmutable();
                     throwIfBadString(str);
-                    appendStringToMapLocked(str);
-                    nativeSetByteBlock(mPtr, mBlockHash, mStringBlock);
-                    updateStringMapLocked();
+                    byte[] block = new byte[mMaxByte];
+                    nativeGetByteBlock(mPtr, 0, block);
+                    appendStringToMapLocked(str, block);
+                    nativeSetByteBlock(mPtr, mBlockHash, block);
+                    updateStringMapLocked(block);
                     handle = mStringHandle.get(str);
                 }
                 return handle;
@@ -2038,6 +2060,7 @@
      * @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);
 
     /**
@@ -2046,6 +2069,7 @@
      * @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);
 
     /**
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index c6b8f3b..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;
@@ -1775,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/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 081ce31..0a4d8f2 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -340,6 +340,12 @@
     public int requestedVisibleTypes;
 
     /**
+     * Whether the top activity has requested to limit educational dialogs shown by the system.
+     * @hide
+     */
+    public boolean isTopActivityLimitSystemEducationDialogs;
+
+    /**
      * Encapsulate specific App Compat information.
      * @hide
      */
@@ -486,6 +492,8 @@
                 && Objects.equals(capturedLink, that.capturedLink)
                 && capturedLinkTimestamp == that.capturedLinkTimestamp
                 && requestedVisibleTypes == that.requestedVisibleTypes
+                && isTopActivityLimitSystemEducationDialogs
+                    == that.isTopActivityLimitSystemEducationDialogs
                 && appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo)
                 && Objects.equals(topActivityMainWindowFrame, that.topActivityMainWindowFrame);
     }
@@ -562,6 +570,7 @@
         capturedLink = source.readTypedObject(Uri.CREATOR);
         capturedLinkTimestamp = source.readLong();
         requestedVisibleTypes = source.readInt();
+        isTopActivityLimitSystemEducationDialogs = source.readBoolean();
         appCompatTaskInfo = source.readTypedObject(AppCompatTaskInfo.CREATOR);
         topActivityMainWindowFrame = source.readTypedObject(Rect.CREATOR);
     }
@@ -616,6 +625,7 @@
         dest.writeTypedObject(capturedLink, flags);
         dest.writeLong(capturedLinkTimestamp);
         dest.writeInt(requestedVisibleTypes);
+        dest.writeBoolean(isTopActivityLimitSystemEducationDialogs);
         dest.writeTypedObject(appCompatTaskInfo, flags);
         dest.writeTypedObject(topActivityMainWindowFrame, flags);
     }
@@ -660,6 +670,8 @@
                 + " capturedLink=" + capturedLink
                 + " capturedLinkTimestamp=" + capturedLinkTimestamp
                 + " requestedVisibleTypes=" + requestedVisibleTypes
+                + " isTopActivityLimitSystemEducationDialogs="
+                + isTopActivityLimitSystemEducationDialogs
                 + " appCompatTaskInfo=" + appCompatTaskInfo
                 + " topActivityMainWindowFrame=" + topActivityMainWindowFrame
                 + "}";
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/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 014e4660..479f3df 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -21,10 +21,12 @@
 import static android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
 
 import static com.android.window.flags.Flags.FLAG_MULTI_CROP;
 import static com.android.window.flags.Flags.multiCrop;
 
+import android.Manifest;
 import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
@@ -39,6 +41,8 @@
 import android.annotation.TestApi;
 import android.annotation.UiContext;
 import android.app.compat.CompatChanges;
+import android.app.wallpaper.WallpaperDescription;
+import android.app.wallpaper.WallpaperInstance;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -319,12 +323,12 @@
      * This is only used internally by the framework and the WallpaperBackupAgent.
      * @hide
      */
-    @IntDef(value = {
+    @IntDef(prefix = { "ORIENTATION_" }, value = {
             ORIENTATION_UNKNOWN,
-            PORTRAIT,
-            LANDSCAPE,
-            SQUARE_PORTRAIT,
-            SQUARE_LANDSCAPE,
+            ORIENTATION_PORTRAIT,
+            ORIENTATION_LANDSCAPE,
+            ORIENTATION_SQUARE_PORTRAIT,
+            ORIENTATION_SQUARE_LANDSCAPE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ScreenOrientation {}
@@ -338,25 +342,25 @@
      * Portrait orientation of most screens
      * @hide
      */
-    public static final int PORTRAIT = 0;
+    public static final int ORIENTATION_PORTRAIT = 0;
 
     /**
      * Landscape orientation of most screens
      * @hide
      */
-    public static final int LANDSCAPE = 1;
+    public static final int ORIENTATION_LANDSCAPE = 1;
 
     /**
      * Portrait orientation with similar width and height (e.g. the inner screen of a foldable)
      * @hide
      */
-    public static final int SQUARE_PORTRAIT = 2;
+    public static final int ORIENTATION_SQUARE_PORTRAIT = 2;
 
     /**
      * Landscape orientation with similar width and height (e.g. the inner screen of a foldable)
      * @hide
      */
-    public static final int SQUARE_LANDSCAPE = 3;
+    public static final int ORIENTATION_SQUARE_LANDSCAPE = 3;
 
     /**
      * Converts a (width, height) screen size to a {@link ScreenOrientation}.
@@ -367,10 +371,10 @@
     public static @ScreenOrientation int getOrientation(Point screenSize) {
         float ratio = ((float) screenSize.x) / screenSize.y;
         // ratios between 3/4 and 4/3 are considered square
-        return ratio >= 4 / 3f ? LANDSCAPE
-                : ratio > 1f ? SQUARE_LANDSCAPE
-                : ratio > 3 / 4f ? SQUARE_PORTRAIT
-                : PORTRAIT;
+        return ratio >= 4 / 3f ? ORIENTATION_LANDSCAPE
+                : ratio > 1f ? ORIENTATION_SQUARE_LANDSCAPE
+                : ratio > 3 / 4f ? ORIENTATION_SQUARE_PORTRAIT
+                : ORIENTATION_PORTRAIT;
     }
 
     /**
@@ -379,10 +383,10 @@
      */
     public static @ScreenOrientation int getRotatedOrientation(@ScreenOrientation int orientation) {
         switch (orientation) {
-            case PORTRAIT: return LANDSCAPE;
-            case LANDSCAPE: return PORTRAIT;
-            case SQUARE_PORTRAIT: return SQUARE_LANDSCAPE;
-            case SQUARE_LANDSCAPE: return SQUARE_PORTRAIT;
+            case ORIENTATION_PORTRAIT: return ORIENTATION_LANDSCAPE;
+            case ORIENTATION_LANDSCAPE: return ORIENTATION_PORTRAIT;
+            case ORIENTATION_SQUARE_PORTRAIT: return ORIENTATION_SQUARE_LANDSCAPE;
+            case ORIENTATION_SQUARE_LANDSCAPE: return ORIENTATION_SQUARE_PORTRAIT;
             default: return ORIENTATION_UNKNOWN;
         }
     }
@@ -2023,6 +2027,34 @@
     }
 
     /**
+     * Returns the description of the designated wallpaper. Returns null if the lock screen
+     * wallpaper is requested lock screen wallpaper is not set.
+
+     * @param which Specifies wallpaper to request (home or lock).
+     * @throws IllegalArgumentException if {@code which} is not exactly one of
+     * {{@link #FLAG_SYSTEM},{@link #FLAG_LOCK}}.
+     *
+     * @hide
+     */
+    @Nullable
+    @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+    @RequiresPermission(READ_WALLPAPER_INTERNAL)
+    @SystemApi
+    public WallpaperInstance getWallpaperInstance(@SetWallpaperFlags int which) {
+        checkExactlyOneWallpaperFlagSet(which);
+        try {
+            if (sGlobals.mService == null) {
+                Log.w(TAG, "WallpaperService not running");
+                throw new RuntimeException(new DeadSystemException());
+            } else {
+                return sGlobals.mService.getWallpaperInstance(which, mContext.getUserId());
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get an open, readable file descriptor for the file that contains metadata about the
      * context user's wallpaper.
      *
@@ -2955,15 +2987,66 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT)
+    @RequiresPermission(allOf = {android.Manifest.permission.SET_WALLPAPER_COMPONENT,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true)
     public boolean setWallpaperComponentWithFlags(@NonNull ComponentName name,
             @SetWallpaperFlags int which, int userId) {
+        WallpaperDescription description = new WallpaperDescription.Builder().setComponent(
+                name).build();
+        return setWallpaperComponentWithDescription(description, which, userId);
+    }
+
+    /**
+     * Set the implementation of {@link android.service.wallpaper.WallpaperService} used to render
+     * wallpaper, along with associated metadata.
+     *
+     * <p>This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT
+     * permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change
+     * another user's wallpaper.
+     * </p>
+     *
+     * @param description wallpaper component and metadata to set
+     * @param which Specifies wallpaper destination (home and/or lock).
+     * @return true on success, otherwise false
+     *
+     * @hide
+     */
+    @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+    @SystemApi
+    @RequiresPermission(allOf = {android.Manifest.permission.SET_WALLPAPER_COMPONENT,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true)
+    public boolean setWallpaperComponentWithDescription(@NonNull WallpaperDescription description,
+            @SetWallpaperFlags int which) {
+        return setWallpaperComponentWithDescription(description, which, mContext.getUserId());
+    }
+
+    /**
+     * Set the implementation of {@link android.service.wallpaper.WallpaperService} used to render
+     * wallpaper, along with associated metadata.
+     *
+     * <p>This can only be called by packages with android.permission.SET_WALLPAPER_COMPONENT
+     * permission. The caller must hold the INTERACT_ACROSS_USERS_FULL permission to change
+     * another user's wallpaper.
+     * </p>
+     *
+     * @param description wallpaper component and metadata to set
+     * @param which Specifies wallpaper destination (home and/or lock).
+     * @param userId User for whom the component should be set.
+     * @return true on success, otherwise false
+     *
+     * @hide
+     */
+    @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+    @RequiresPermission(allOf = {android.Manifest.permission.SET_WALLPAPER_COMPONENT,
+            Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional = true)
+    public boolean setWallpaperComponentWithDescription(@NonNull WallpaperDescription description,
+            @SetWallpaperFlags int which, int userId) {
         if (sGlobals.mService == null) {
             Log.w(TAG, "WallpaperManagerService not running");
             throw new RuntimeException(new DeadSystemException());
         }
         try {
-            sGlobals.mService.setWallpaperComponentChecked(name, mContext.getOpPackageName(),
+            sGlobals.mService.setWallpaperComponentChecked(description, mContext.getOpPackageName(),
                     which, userId);
             return true;
         } catch (RemoteException e) {
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index fd58377..3aaca25 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -341,3 +341,11 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+    name: "set_mte_policy_coexistence"
+    is_exported: true
+    namespace: "enterprise"
+    description: "Enables coexistence support for Setting MTE policy."
+    bug: "376213673"
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 5b478d09..5ddb590 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -47,32 +47,42 @@
  * <p>An app function is a piece of functionality that apps expose to the system for cross-app
  * orchestration.
  *
- * <p>**Developer Workflow:**
+ * <p>**Building App Functions:**
  *
- * <p>Most developers should interact with 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>Most developers should build app functions through the AppFunctions SDK. This SDK library
+ * offers a more convenient and type-safe way to build app functions. The SDK provides predefined
+ * function schemas for common use cases and associated data classes for function parameters and
+ * return values. Apps only have to implement the provided interfaces. Internally, the SDK converts
+ * these data classes into {@link ExecuteAppFunctionRequest#getParameters()} and {@link
+ * ExecuteAppFunctionResponse#getResultDocument()}.
  *
- * <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>**Discovering 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.
+ * indexed on-device by {@link AppSearchManager}. AppSearch stores the indexed information as an
+ * {@code AppFunctionStaticMetadata} document. This document contains the {@code functionIdentifier}
+ * and the schema information that the app function implements. 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.
+ * AppFunction SDK provides a convenient way to achieve this and is the preferred method.
  *
  * <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
+ * <p>To execute an app function, the caller app can retrieve the {@code functionIdentifier} from
+ * the {@code AppFunctionStaticMetadata} document and use it to build an {@link
+ * ExecuteAppFunctionRequest}. Then, invoke {@link #executeAppFunction} with the request to execute
+ * the app function. 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}.
+ * apps. An app can always execute its own app functions and doesn't need these permissions.
+ * AppFunction SDK provides a convenient way to achieve this and is the preferred method.
+ *
+ * <p>**Example:**
+ *
+ * <p>An assistant app is trying to fulfill the user request "Save XYZ into my note". The assistant
+ * app should first list all available app functions as {@code AppFunctionStaticMetadata} documents
+ * from AppSearch. Then, it should identify an app function that implements the {@code CreateNote}
+ * schema. Finally, the assistant app can invoke {@link #executeAppFunction} with the {@code
+ * functionIdentifier} of the chosen function.
  */
 @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
 @SystemService(Context.APP_FUNCTION_SERVICE)
@@ -202,12 +212,13 @@
     /**
      * 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
+     * <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:
+     * <p>If the 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
@@ -221,23 +232,47 @@
      * @param executor the executor to run the request
      * @param callback the callback to receive the function enabled check result
      */
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+                Manifest.permission.EXECUTE_APP_FUNCTIONS
+            },
+            conditional = true)
     public void isAppFunctionEnabled(
             @NonNull String functionIdentifier,
             @NonNull String targetPackage,
             @NonNull Executor executor,
             @NonNull OutcomeReceiver<Boolean, Exception> callback) {
-        Objects.requireNonNull(functionIdentifier);
-        Objects.requireNonNull(targetPackage);
-        Objects.requireNonNull(executor);
-        Objects.requireNonNull(callback);
-        AppSearchManager appSearchManager = mContext.getSystemService(AppSearchManager.class);
-        if (appSearchManager == null) {
-            callback.onError(new IllegalStateException("Failed to get AppSearchManager."));
-            return;
-        }
+        isAppFunctionEnabledInternal(functionIdentifier, targetPackage, executor, callback);
+    }
 
-        AppFunctionManagerHelper.isAppFunctionEnabled(
-                functionIdentifier, targetPackage, appSearchManager, executor, callback);
+    /**
+     * 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, unlike {@link
+     * #isAppFunctionEnabled(String, String, Executor, OutcomeReceiver)}, which allows specifying a
+     * different target package.
+     *
+     * <p>If the 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 executor the executor to run the request
+     * @param callback the callback to receive the function enabled check result
+     */
+    public void isAppFunctionEnabled(
+            @NonNull String functionIdentifier,
+            @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+        isAppFunctionEnabledInternal(
+                functionIdentifier, mContext.getPackageName(), executor, callback);
     }
 
     /**
@@ -280,6 +315,25 @@
         }
     }
 
+    private void isAppFunctionEnabledInternal(
+            @NonNull String functionIdentifier,
+            @NonNull String targetPackage,
+            @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+        Objects.requireNonNull(functionIdentifier);
+        Objects.requireNonNull(targetPackage);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        AppSearchManager appSearchManager = mContext.getSystemService(AppSearchManager.class);
+        if (appSearchManager == null) {
+            callback.onError(new IllegalStateException("Failed to get AppSearchManager."));
+            return;
+        }
+
+        AppFunctionManagerHelper.isAppFunctionEnabled(
+                functionIdentifier, targetPackage, appSearchManager, executor, callback);
+    }
+
     private static class CallbackWrapper extends IAppFunctionEnabledCallback.Stub {
 
         private final OutcomeReceiver<Void, Exception> mCallback;
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
index 4c5e8c1..41bb622 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
@@ -114,18 +114,14 @@
      * <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.
+     * @see AppFunctionManager on how to determine the expected parameters.
      */
     @NonNull
     public GenericDocument getParameters() {
         return mParameters.getValue();
     }
 
-    /** Returns the additional data relevant to this function execution. */
+    /** Returns the additional metadata for this function execution request. */
     @NonNull
     public Bundle getExtras() {
         return mExtras;
@@ -153,19 +149,31 @@
         @NonNull
         private GenericDocument mParameters = new GenericDocument.Builder<>("", "", "").build();
 
+        /**
+         * Creates a new instance of this builder class.
+         *
+         * @param targetPackageName The package name of the target app providing the app function to
+         *     invoke.
+         * @param functionIdentifier The identifier used by the {@link AppFunctionService} from the
+         *     target app to uniquely identify the function to be invoked.
+         */
         public Builder(@NonNull String targetPackageName, @NonNull String functionIdentifier) {
             mTargetPackageName = Objects.requireNonNull(targetPackageName);
             mFunctionIdentifier = Objects.requireNonNull(functionIdentifier);
         }
 
-        /** Sets the additional data relevant to this function execution. */
+        /** Sets the additional metadata for this function execution request. */
         @NonNull
         public Builder setExtras(@NonNull Bundle extras) {
             mExtras = Objects.requireNonNull(extras);
             return this;
         }
 
-        /** Sets the function parameters. */
+        /**
+         * Sets the function parameters.
+         *
+         * @see #ExecuteAppFunctionRequest#getParameters()
+         */
         @NonNull
         public Builder setParameters(@NonNull GenericDocument parameters) {
             Objects.requireNonNull(parameters);
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
index c907ef1..cdf02e6 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -73,38 +73,102 @@
      */
     public static final String PROPERTY_RETURN_VALUE = "returnValue";
 
-    /** The call was successful. */
+    /**
+     * The call was successful.
+     *
+     * <p>This result code does not belong in an error category.
+     */
     public static final int RESULT_OK = 0;
 
-    /** The caller does not have the permission to execute an app function. */
-    public static final int RESULT_DENIED = 1;
+    /**
+     * The caller does not have the permission to execute an app function.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+     */
+    public static final int RESULT_DENIED = 1000;
+
+    /**
+     * The caller supplied invalid arguments to the execution request.
+     *
+     * <p>This error may be considered similar to {@link IllegalArgumentException}.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+     */
+    public static final int RESULT_INVALID_ARGUMENT = 1001;
+
+    /**
+     * The caller tried to execute a disabled app function.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+     */
+    public static final int RESULT_DISABLED = 1002;
+
+    /**
+     * The caller tried to execute a function that does not exist.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+     */
+    public static final int RESULT_FUNCTION_NOT_FOUND = 1003;
+
+    /**
+     * An internal unexpected error coming from the system.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+     */
+    public static final int RESULT_SYSTEM_ERROR = 2000;
+
+    /**
+     * The operation was cancelled. Use this error code to report that a cancellation is done after
+     * receiving a cancellation signal.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+     */
+    public static final int RESULT_CANCELLED = 2001;
 
     /**
      * 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 unexpected error coming from the system. */
-    public static final int RESULT_INTERNAL_ERROR = 3;
-
-    /**
-     * The caller supplied invalid arguments to the call.
      *
-     * <p>This error may be considered similar to {@link IllegalArgumentException}.
+     * <p>This error is in the {@link #ERROR_CATEGORY_APP} category.
      */
-    public static final int RESULT_INVALID_ARGUMENT = 4;
-
-    /** The caller tried to execute a disabled app function. */
-    public static final int RESULT_DISABLED = 5;
+    public static final int RESULT_APP_UNKNOWN_ERROR = 3000;
 
     /**
-     * The operation was cancelled. Use this error code to report that a cancellation is done after
-     * receiving a cancellation signal.
+     * The error category is unknown.
+     *
+     * <p>This is the default value for {@link #getErrorCategory}.
      */
-    public static final int RESULT_CANCELLED = 6;
+    public static final int ERROR_CATEGORY_UNKNOWN = 0;
+
+    /**
+     * The error is caused by the app requesting a function execution.
+     *
+     * <p>For example, the caller provided invalid parameters in the execution request e.g. an
+     * invalid function ID.
+     *
+     * <p>Errors in the category fall in the range 1000-1999 inclusive.
+     */
+    public static final int ERROR_CATEGORY_REQUEST_ERROR = 1;
+
+    /**
+     * The error is caused by an issue in the system.
+     *
+     * <p>For example, the AppFunctionService implementation is not found by the system.
+     *
+     * <p>Errors in the category fall in the range 2000-2999 inclusive.
+     */
+    public static final int ERROR_CATEGORY_SYSTEM = 2;
+
+    /**
+     * The error is caused by the app providing the function.
+     *
+     * <p>For example, the app crashed when the system is executing the request.
+     *
+     * <p>Errors in the category fall in the range 3000-3999 inclusive.
+     */
+    public static final int ERROR_CATEGORY_APP = 3;
 
     /** The result code of the app function execution. */
     @ResultCode private final int mResultCode;
@@ -156,7 +220,7 @@
      * Returns a successful response.
      *
      * @param resultDocument The return value of the executed function.
-     * @param extras The additional metadata data relevant to this function execution response.
+     * @param extras The additional metadata for this function execution response.
      */
     @NonNull
     @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
@@ -174,7 +238,7 @@
      * Returns a failure response.
      *
      * @param resultCode The result code of the app function execution.
-     * @param extras The additional metadata data relevant to this function execution response.
+     * @param extras The additional metadata for this function execution response.
      * @param errorMessage The error message associated with the result, if any.
      */
     @NonNull
@@ -198,6 +262,36 @@
     }
 
     /**
+     * Returns the error category of the {@link ExecuteAppFunctionResponse}.
+     *
+     * <p>This method categorizes errors based on their underlying cause, allowing developers to
+     * implement targeted error handling and provide more informative error messages to users. It
+     * maps ranges of result codes to specific error categories.
+     *
+     * <p>When constructing a {@link #newFailure} response, use the appropriate result code value to
+     * ensure correct categorization of the failed response.
+     *
+     * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the result code does not belong to
+     * any error category, for example, in the case of a successful result with {@link #RESULT_OK}.
+     *
+     * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding
+     * result code ranges.
+     */
+    @ErrorCategory
+    public int getErrorCategory() {
+        if (mResultCode >= 1000 && mResultCode < 2000) {
+            return ERROR_CATEGORY_REQUEST_ERROR;
+        }
+        if (mResultCode >= 2000 && mResultCode < 3000) {
+            return ERROR_CATEGORY_SYSTEM;
+        }
+        if (mResultCode >= 3000 && mResultCode < 4000) {
+            return ERROR_CATEGORY_APP;
+        }
+        return ERROR_CATEGORY_UNKNOWN;
+    }
+
+    /**
      * Returns a generic document containing the return value of the executed function.
      *
      * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
@@ -216,13 +310,15 @@
      *       // Do something with the returnValue
      *     }
      * </pre>
+     *
+     * @see AppFunctionManager on how to determine the expected function return.
      */
     @NonNull
     public GenericDocument getResultDocument() {
         return mResultDocumentWrapper.getValue();
     }
 
-    /** Returns the extras of the app function execution. */
+    /** Returns the additional metadata for this function execution response. */
     @NonNull
     public Bundle getExtras() {
         return mExtras;
@@ -278,11 +374,28 @@
                 RESULT_OK,
                 RESULT_DENIED,
                 RESULT_APP_UNKNOWN_ERROR,
-                RESULT_INTERNAL_ERROR,
+                RESULT_FUNCTION_NOT_FOUND,
+                RESULT_SYSTEM_ERROR,
                 RESULT_INVALID_ARGUMENT,
                 RESULT_DISABLED,
                 RESULT_CANCELLED
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ResultCode {}
+
+    /**
+     * Error categories.
+     *
+     * @hide
+     */
+    @IntDef(
+            prefix = {"ERROR_CATEGORY_"},
+            value = {
+                ERROR_CATEGORY_UNKNOWN,
+                ERROR_CATEGORY_REQUEST_ERROR,
+                ERROR_CATEGORY_APP,
+                ERROR_CATEGORY_SYSTEM
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ErrorCategory {}
 }
diff --git a/core/java/android/app/backup/NotificationLoggingConstants.java b/core/java/android/app/backup/NotificationLoggingConstants.java
new file mode 100644
index 0000000..3601e86
--- /dev/null
+++ b/core/java/android/app/backup/NotificationLoggingConstants.java
@@ -0,0 +1,79 @@
+/*
+ * 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.backup;
+
+/**
+ * @hide
+ */
+public class NotificationLoggingConstants {
+
+    // Key under which the payload blob is stored
+    public static final String KEY_NOTIFICATIONS = "notifications";
+
+    /**
+     * Events for android.service.notification.ZenModeConfig - the configuration for all modes
+     * settings for a single user
+     */
+    @BackupRestoreEventLogger.BackupRestoreDataType
+    public static final String DATA_TYPE_ZEN_CONFIG = KEY_NOTIFICATIONS + ":zen_config";
+    /**
+     * Events for android.service.notification.ZenModeConfig.ZenRule - a single mode within a
+     * ZenModeConfig
+     */
+    @BackupRestoreEventLogger.BackupRestoreDataType
+    public static final String DATA_TYPE_ZEN_RULES = KEY_NOTIFICATIONS + ":zen_rules";
+    /**
+     * Events for globally stored notifications data that aren't stored in settings, like whether
+     * to hide silent notification in the status bar
+     */
+    @BackupRestoreEventLogger.BackupRestoreDataType
+    public static final String DATA_TYPE_NOTIF_GLOBAL = KEY_NOTIFICATIONS + ":global";
+    /**
+     * Events for package specific notification settings, including app and
+     * android.app.NotificationChannel level settings.
+     */
+    @BackupRestoreEventLogger.BackupRestoreDataType
+    public static final String DATA_TYPE_NOTIF_PACKAGES = KEY_NOTIFICATIONS + ":packages";
+    /**
+     * Events for approved ManagedServices (NotificationListenerServices,
+     * NotificationAssistantService, ConditionProviderService).
+     */
+    @BackupRestoreEventLogger.BackupRestoreDataType
+    public static final String DATA_TYPE_MANAGED_SERVICE_PRIMARY_APPROVED = KEY_NOTIFICATIONS +
+            ":managed_service_primary_approved";
+    /**
+     * Events for what types of notifications NotificationListenerServices cannot see
+     */
+    @BackupRestoreEventLogger.BackupRestoreDataType
+    public static final String DATA_TYPE_NLS_RESTRICTED = KEY_NOTIFICATIONS +
+            ":nls_restricted";
+    /**
+     * Events for ManagedServices that are approved because they have a different primary
+     * ManagedService (ConditionProviderService).
+     */
+    @BackupRestoreEventLogger.BackupRestoreDataType
+    public static final String DATA_TYPE_MANAGED_SERVICE_SECONDARY_APPROVED = KEY_NOTIFICATIONS +
+            ":managed_service_secondary_approved";
+    /**
+     * Events for individual snoozed notifications.
+     */
+    @BackupRestoreEventLogger.BackupRestoreDataType
+    public static final String DATA_TYPE_SNOOZED = KEY_NOTIFICATIONS + ":snoozed";
+
+
+    @BackupRestoreEventLogger.BackupRestoreError
+    public static final String ERROR_XML_PARSING = KEY_NOTIFICATIONS + ":invalid_xml_parsing";
+}
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index 3b0c867..5e09517 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -12,4 +12,18 @@
   namespace: "machine_learning"
   description: "Flag to refresh the token to the callback"
   bug: "309689654"
+}
+
+flag {
+    name: "multi_window_screen_context"
+    namespace: "machine_learning"
+    description: "Report screen context and positions for all windows."
+    bug: "371065456"
+}
+
+flag {
+    name: "contextual_search_window_layer"
+    namespace: "machine_learning"
+    description: "Identify live contextual search UI to exclude from contextual search screenshot."
+    bug: "372510690"
 }
\ No newline at end of file
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
index 7c6989e..f51f748 100644
--- a/core/java/android/app/performance.aconfig
+++ b/core/java/android/app/performance.aconfig
@@ -7,5 +7,14 @@
      is_exported: true
      is_fixed_read_only: true
      description: "PropertyInvalidatedCache uses shared memory for nonces."
-     bug: "366552454"
+     bug: "360897450"
+}
+
+flag {
+     namespace: "system_performance"
+     name: "enforce_pic_testmode_protocol"
+     is_exported: true
+     is_fixed_read_only: true
+     description: "Enforce PropertyInvalidatedCache.setTestMode() protocol"
+     bug: "360897450"
 }
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 740f593..730bb73 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -41,5 +41,6 @@
     void unlockedByBiometricForUser(int userId, in BiometricSourceType source);
     void clearAllBiometricRecognized(in BiometricSourceType target, int unlockedUser);
     boolean isActiveUnlockRunning(int userId);
+    @EnforcePermission("ACCESS_FINE_LOCATION")
     boolean isInSignificantPlace();
 }
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 88d4d69..1ef83cd 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -305,6 +305,7 @@
      *
      * @hide
      */
+    @RequiresPermission(Manifest.permission.ACCESS_FINE_LOCATION)
     public boolean isInSignificantPlace() {
         try {
             return mService.isInSignificantPlace();
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..c3d6340
--- /dev/null
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -0,0 +1,471 @@
+/*
+ * 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 static java.nio.charset.StandardCharsets.UTF_8;
+
+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 android.util.Xml;
+
+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.io.InputStream;
+import java.io.OutputStream;
+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);
+    }
+
+    ////// Stream read/write
+
+    /**
+     * Writes the content of the {@link WallpaperDescription} to a {@link OutputStream}.
+     *
+     * <p>The content can be read by {@link #readFromStream}. This method is intended for use by
+     * trusted apps only, and the format is not guaranteed to be stable.</p>
+     */
+    public void writeToStream(@NonNull OutputStream outputStream) throws IOException {
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
+        serializer.setOutput(outputStream, UTF_8.name());
+        serializer.startTag(null, "description");
+        try {
+            saveToXml(serializer);
+        } catch (XmlPullParserException e) {
+            throw new IOException(e);
+        }
+        serializer.endTag(null, "description");
+        serializer.flush();
+    }
+
+    /**
+     * Reads a {@link PersistableBundle} from an {@link InputStream}.
+     *
+     * <p>The stream must be generated by {@link #writeToStream}. This method is intended for use by
+     * trusted apps only, and the format is not guaranteed to be stable.</p>
+     */
+    @NonNull
+    public static WallpaperDescription readFromStream(@NonNull InputStream inputStream)
+            throws IOException {
+        try {
+            TypedXmlPullParser parser = Xml.newFastPullParser();
+            parser.setInput(inputStream, UTF_8.name());
+            parser.next();
+            return WallpaperDescription.restoreFromXml(parser);
+        } catch (XmlPullParserException e) {
+            throw new IOException(e);
+        }
+    }
+
+    ////// 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);
+    }
+
+    @NonNull
+    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/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 72f992a..79eeaa9 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -306,6 +306,38 @@
     }
 
     /**
+     * Returns an {@link IntentSender} for starting the configuration activity for the widget.
+     *
+     * @return The {@link IntentSender} or null if service is currently unavailable
+     *
+     * @throws android.content.ActivityNotFoundException If configuration activity is not found.
+     *
+     * @see #startAppWidgetConfigureActivityForResult
+     *
+     * @hide
+     */
+    @Nullable
+    public final IntentSender getIntentSenderForConfigureActivity(int appWidgetId,
+            int intentFlags)  {
+        if (sService == null) {
+            return null;
+        }
+
+        IntentSender intentSender;
+        try {
+            intentSender = sService.createAppWidgetConfigIntentSender(mContextOpPackageName,
+                    appWidgetId, intentFlags);
+        } catch (RemoteException e) {
+            throw new RuntimeException("system server dead?", e);
+        }
+
+        if (intentSender == null) {
+            throw new ActivityNotFoundException();
+        }
+        return intentSender;
+    }
+
+    /**
      * Starts an app widget provider configure activity for result on behalf of the caller.
      * Use this method if the provider is in another profile as you are not allowed to start
      * an activity in another profile. You can optionally provide a request code that is
@@ -330,18 +362,11 @@
             return;
         }
         try {
-            IntentSender intentSender = sService.createAppWidgetConfigIntentSender(
-                    mContextOpPackageName, appWidgetId, intentFlags);
-            if (intentSender != null) {
-                activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0,
-                        options);
-            } else {
-                throw new ActivityNotFoundException();
-            }
+            IntentSender intentSender = getIntentSenderForConfigureActivity(appWidgetId,
+                    intentFlags);
+            activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0, options);
         } catch (IntentSender.SendIntentException e) {
             throw new ActivityNotFoundException();
-        } catch (RemoteException e) {
-            throw new RuntimeException("system server dead?", e);
         }
     }
 
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index d8142fd..8f20ea0 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -246,7 +246,8 @@
      * this widget. Can have the value {@link
      * AppWidgetProviderInfo#WIDGET_CATEGORY_HOME_SCREEN} or {@link
      * AppWidgetProviderInfo#WIDGET_CATEGORY_KEYGUARD} or {@link
-     * AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}.
+     * AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX} or {@link
+     * AppWidgetProviderInfo#WIDGET_CATEGORY_NOT_KEYGUARD}.
      */
     public static final String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
 
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 1a80cac2..75c76d1 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -93,11 +93,26 @@
      */
     public static final int WIDGET_CATEGORY_SEARCHBOX = 4;
 
+    /**
+     * Indicates that the widget should never be shown on the keyguard.
+     *
+     * <p>Some keyguard style features may decide that {@link #WIDGET_CATEGORY_KEYGUARD} isn't
+     * required to be added by an app to show on the feature when chosen by a user.
+     * This category allows for a stronger statement about placement of the widget that, even in the
+     * above case, this widget should not be offered on the keyguard.
+     *
+     * <p>Setting this category doesn't change the behavior of AppWidgetManager queries, it is the
+     * responsibility of the widget surface to respect this value.
+     */
+    @FlaggedApi(android.appwidget.flags.Flags.FLAG_NOT_KEYGUARD_CATEGORY)
+    public static final int WIDGET_CATEGORY_NOT_KEYGUARD = 8;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
             WIDGET_CATEGORY_HOME_SCREEN,
             WIDGET_CATEGORY_KEYGUARD,
             WIDGET_CATEGORY_SEARCHBOX,
+            WIDGET_CATEGORY_NOT_KEYGUARD,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CategoryFlags {}
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index e5c94fc..940a4d2 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -55,7 +55,7 @@
   name: "remote_views_proto"
   namespace: "app_widgets"
   description: "Enable support for persisting RemoteViews previews to Protobuf"
-  bug: "364420494"
+  bug: "306546610"
 }
 
 flag {
@@ -83,3 +83,12 @@
   is_exported: true
   is_fixed_read_only: true
 }
+
+flag {
+  name: "not_keyguard_category"
+  namespace: "app_widgets"
+  description: "Adds the NOT_KEYGUARD category to AppWidgetInfo categories"
+  bug: "365169792"
+  is_exported: true
+  is_fixed_read_only: true
+}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 9eb6d56..9af2016 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -145,3 +145,11 @@
     bug: "370657575"
     is_exported: true
 }
+
+flag {
+    namespace: "virtual_devices"
+    name: "default_device_camera_access_policy"
+    description: "API for default device camera access policy"
+    bug: "371173368"
+    is_exported: true
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 07106e8..186f7b3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3658,6 +3658,27 @@
     public abstract void unregisterReceiver(BroadcastReceiver receiver);
 
     /**
+     * Returns the list of {@link IntentFilter} objects that have been registered for the given
+     * {@link BroadcastReceiver}.
+     *
+     * @param receiver The {@link BroadcastReceiver} whose registered intent filters
+     *                 should be retrieved.
+     *
+     * @return A list of registered intent filters, or an empty list if the given receiver is not
+     *         registered.
+     *
+     * @throws NullPointerException if the {@code receiver} is {@code null}.
+     *
+     * @hide
+     */
+    @SuppressLint("UnflaggedApi") // TestApi
+    @TestApi
+    @NonNull
+    public List<IntentFilter> getRegisteredIntentFilters(@NonNull BroadcastReceiver receiver) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Request that a given application service be started.  The Intent
      * should either contain the complete class name of a specific service
      * implementation to start, or a specific package name to target.  If the
@@ -4325,6 +4346,7 @@
            //@hide: ECM_ENHANCED_CONFIRMATION_SERVICE,
             CONTACT_KEYS_SERVICE,
             RANGING_SERVICE,
+            MEDIA_QUALITY_SERVICE,
             ADVANCED_PROTECTION_SERVICE,
 
     })
@@ -4872,6 +4894,8 @@
      * @see android.net.vcn.VcnManager
      * @hide
      */
+    @FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API)
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final String VCN_MANAGEMENT_SERVICE = "vcn_management";
 
     /**
@@ -6653,8 +6677,8 @@
      *
      * @see #getSystemService(String)
      * @see android.telephony.satellite.SatelliteManager
-     * @hide
      */
+    @FlaggedApi(com.android.internal.telephony.flags.Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)
     public static final String SATELLITE_SERVICE = "satellite";
 
     /**
@@ -6767,6 +6791,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/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 79fa6ea..23d17cb 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -843,6 +843,13 @@
         mBase.unregisterReceiver(receiver);
     }
 
+    /** @hide */
+    @Override
+    @NonNull
+    public List<IntentFilter> getRegisteredIntentFilters(@NonNull BroadcastReceiver receiver) {
+        return mBase.getRegisteredIntentFilters(receiver);
+    }
+
     @Override
     public @Nullable ComponentName startService(Intent service) {
         return mBase.startService(service);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 66ef004..6fa5a9b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -41,10 +41,7 @@
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.StatusBarManager;
-import android.app.compat.CompatChanges;
 import android.bluetooth.BluetoothDevice;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.Overridable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -673,12 +670,6 @@
 @android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class Intent implements Parcelable, Cloneable {
     private static final String TAG = "Intent";
-
-    /** @hide */
-    @ChangeId
-    @Overridable
-    public static final long ENABLE_PREVENT_INTENT_REDIRECT = 29076063L;
-
     private static final String ATTR_ACTION = "action";
     private static final String TAG_CATEGORIES = "categories";
     private static final String ATTR_CATEGORY = "category";
@@ -908,7 +899,7 @@
         boolean isForeign = (intent.mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0;
         boolean isWithoutTrustedCreatorToken =
                 (intent.mLocalFlags & Intent.LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT) == 0;
-        if (isForeign && isWithoutTrustedCreatorToken) {
+        if (isForeign && isWithoutTrustedCreatorToken && preventIntentRedirect()) {
             intent.addExtendedFlags(EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN);
         }
     }
@@ -6184,7 +6175,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 +6184,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";
 
@@ -12257,7 +12246,7 @@
      * @hide
      */
     public void collectExtraIntentKeys() {
-        if (!isPreventIntentRedirectEnabled()) return;
+        if (!preventIntentRedirect()) return;
 
         if (mExtras != null && !mExtras.isEmpty()) {
             for (String key : mExtras.keySet()) {
@@ -12274,14 +12263,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    public static boolean isPreventIntentRedirectEnabled() {
-        return preventIntentRedirect() && CompatChanges.isChangeEnabled(
-                ENABLE_PREVENT_INTENT_REDIRECT);
-    }
-
     /** @hide */
     public void checkCreatorToken() {
         if (mExtras == null) return;
@@ -12370,7 +12351,7 @@
             out.writeInt(0);
         }
 
-        if (isPreventIntentRedirectEnabled()) {
+        if (preventIntentRedirect()) {
             if (mCreatorTokenInfo == null) {
                 out.writeInt(0);
             } else {
@@ -12437,7 +12418,7 @@
             mOriginalIntent = new Intent(in);
         }
 
-        if (isPreventIntentRedirectEnabled()) {
+        if (preventIntentRedirect()) {
             if (in.readInt() != 0) {
                 mCreatorTokenInfo = new CreatorTokenInfo();
                 mCreatorTokenInfo.mCreatorToken = in.readStrongBinder();
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/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 35f9cff..528bde8 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -536,7 +536,7 @@
 
 flag {
   name: "ignore_restrictions_when_deleting_private_profile"
-  namespace: "multiuser"
+  namespace: "profile_experiences"
   description: "Ignore any user restrictions when deleting private profiles."
   bug: "350953833"
   metadata {
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 4220590..c83cf96 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -156,6 +156,12 @@
      */
     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.
      */
@@ -185,7 +191,10 @@
             Set<String> requiredSplitTypes, Set<String> splitTypes,
             boolean hasDeviceAdminReceiver, boolean isSdkLibrary,
             List<String> usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
-            String[][] usesSdkLibrariesCertDigests, boolean updatableSystem,
+            String[][] usesSdkLibrariesCertDigests,
+            List<String> usesStaticLibraries, long[] usesStaticLibrariesVersionsMajor,
+            String[][] usesStaticLibrariesCertDigests,
+            boolean updatableSystem,
             String emergencyInstaller, List<SharedLibraryInfo> declaredLibraries) {
         mPath = path;
         mPackageName = packageName;
@@ -223,6 +232,9 @@
         mUsesSdkLibraries = usesSdkLibraries;
         mUsesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor;
         mUsesSdkLibrariesCertDigests = usesSdkLibrariesCertDigests;
+        mUsesStaticLibraries = usesStaticLibraries;
+        mUsesStaticLibrariesVersions = usesStaticLibrariesVersionsMajor;
+        mUsesStaticLibrariesCertDigests = usesStaticLibrariesCertDigests;
         mUpdatableSystem = updatableSystem;
         mEmergencyInstaller = emergencyInstaller;
         mArchivedPackage = null;
@@ -266,6 +278,9 @@
         mUsesSdkLibraries = Collections.emptyList();
         mUsesSdkLibrariesVersionsMajor = null;
         mUsesSdkLibrariesCertDigests = null;
+        mUsesStaticLibraries = Collections.emptyList();
+        mUsesStaticLibrariesVersions = null;
+        mUsesStaticLibrariesCertDigests = null;
         mUpdatableSystem = true;
         mEmergencyInstaller = null;
         mArchivedPackage = archivedPackage;
@@ -602,6 +617,21 @@
         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.
      */
@@ -632,10 +662,10 @@
     }
 
     @DataClass.Generated(
-            time = 1729247366948L,
+            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 @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  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 50d8758..34c3f57 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -91,6 +91,7 @@
     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;
@@ -466,6 +467,11 @@
         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
@@ -580,6 +586,51 @@
                                     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
@@ -753,7 +804,9 @@
                         rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
                         hasDeviceAdminReceiver, isSdkLibrary, usesSdkLibraries,
                         usesSdkLibrariesVersionsMajor, usesSdkLibrariesCertDigests,
-                        updatableSystem, emergencyInstaller, declaredLibraries));
+                        usesStaticLibraries, usesStaticLibrariesVersions,
+                        usesStaticLibrariesCertDigests, updatableSystem, emergencyInstaller,
+                        declaredLibraries));
     }
 
     private static boolean isDeviceAdminReceiver(
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index 79c5973..76b25fe 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -121,6 +121,12 @@
 
     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;
 
     /**
@@ -158,6 +164,9 @@
         mUsesSdkLibraries = baseApk.getUsesSdkLibraries();
         mUsesSdkLibrariesVersionsMajor = baseApk.getUsesSdkLibrariesVersionsMajor();
         mUsesSdkLibrariesCertDigests = baseApk.getUsesSdkLibrariesCertDigests();
+        mUsesStaticLibraries = baseApk.getUsesStaticLibraries();
+        mUsesStaticLibrariesVersions = baseApk.getUsesStaticLibrariesVersions();
+        mUsesStaticLibrariesCertDigests = baseApk.getUsesStaticLibrariesCertDigests();
         mSplitNames = splitNames;
         mSplitTypes = splitTypes;
         mIsFeatureSplits = isFeatureSplits;
@@ -462,6 +471,21 @@
     }
 
     @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;
     }
@@ -475,10 +499,10 @@
     }
 
     @DataClass.Generated(
-            time = 1729248757933L,
+            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<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<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/res/flags.aconfig b/core/java/android/content/res/flags.aconfig
index 0af2f25..e98fc0c 100644
--- a/core/java/android/content/res/flags.aconfig
+++ b/core/java/android/content/res/flags.aconfig
@@ -74,3 +74,12 @@
     description: "Feature flag for passing a dimension to create an frro"
     bug: "369672322"
 }
+
+flag {
+    name: "rro_control_for_android_no_overlayable"
+    is_exported: true
+    namespace: "resource_manager"
+    description: "Allow enabling and disabling RROs targeting android package with no overlayable"
+    bug: "364035303"
+}
+
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index d77e628..75c7e26 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -241,7 +241,8 @@
                     NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME,
                     mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
         } catch (SQLiteCantOpenDatabaseException e) {
-            final StringBuilder message = new StringBuilder(e.getMessage())
+            final StringBuilder message = new StringBuilder("Cannot open database ")
+                    .append("[").append(e.getMessage()).append("]")
                     .append(" '").append(file).append("'")
                     .append(" with flags 0x")
                     .append(Integer.toHexString(mConfiguration.openFlags));
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index e8d7e1e..a7e7a57 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -150,6 +150,7 @@
      * All values are in micro-Tesla (uT) and measure the ambient magnetic field
      * in the X, Y and Z axis.
      *
+     *
      * <h4>{@link android.hardware.Sensor#TYPE_GYROSCOPE Sensor.TYPE_GYROSCOPE}:
      * </h4> All values are in radians/second and measure the rate of rotation
      * around the device's local X, Y and Z axis. The coordinate system is the
@@ -406,10 +407,9 @@
      * </h4>
      *
      * <ul>
-     * <li> values[0]: ambient (room) temperature in degree Celsius.</li>
+     * <li> values[0]: Ambient (room) temperature in degrees Celsius</li>
      * </ul>
      *
-     *
      * <h4>{@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD_UNCALIBRATED
      * Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED}:</h4>
      * Similar to {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD},
@@ -549,8 +549,20 @@
      *  <li> values[0]: 1.0 </li>
      * </ul>
      *
-     *   <h4>{@link android.hardware.Sensor#TYPE_HEART_BEAT
-     * Sensor.TYPE_HEART_BEAT}:</h4>
+     * <h4>{@link android.hardware.Sensor#TYPE_STEP_COUNTER Sensor.TYPE_STEP_COUNTER}:</h4>
+     *
+     * <ul>
+     * <li>values[0]: Number of steps taken by the user since the last reboot while the sensor is
+     * activated</li>
+     * </ul>
+     *
+     * <h4>{@link android.hardware.Sensor#TYPE_STEP_DETECTOR Sensor.TYPE_STEP_DETECTOR}:</h4>
+     *
+     * <ul>
+     * <li>values[0]: Always set to 1.0, representing a single step detected event</li>
+     * </ul>
+     *
+     * <h4>{@link android.hardware.Sensor#TYPE_HEART_BEAT Sensor.TYPE_HEART_BEAT}:</h4>
      *
      * A sensor of this type returns an event everytime a heart beat peak is
      * detected.
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index e3fdd26..3ff21d8 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -22,7 +22,6 @@
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
 import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;
 import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
 
@@ -200,7 +199,6 @@
          * @param logoRes A drawable resource of the logo that will be shown on the prompt.
          * @return This builder.
          */
-        @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) {
@@ -226,7 +224,6 @@
          * @param logoBitmap A bitmap drawable of the logo that will be shown on the prompt.
          * @return This builder.
          */
-        @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) {
@@ -250,7 +247,6 @@
          * @return This builder.
          * @throws IllegalArgumentException If logo description is null.
          */
-        @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoDescription(@NonNull String logoDescription) {
@@ -342,7 +338,6 @@
          * @param view The customized view information.
          * @return This builder.
          */
-        @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         @NonNull
         public BiometricPrompt.Builder setContentView(@NonNull PromptContentView view) {
             mPromptInfo.setContentView(view);
@@ -851,7 +846,6 @@
      *
      * @return The drawable resource of the logo, or 0 if the prompt has no logo resource set.
      */
-    @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
     @DrawableRes
     public int getLogoRes() {
@@ -864,7 +858,6 @@
      *
      * @return The logo bitmap of the prompt, or null if the prompt has no logo bitmap set.
      */
-    @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
     @Nullable
     public Bitmap getLogoBitmap() {
@@ -879,7 +872,6 @@
      * @return The logo description of the prompt, or null if the prompt has no logo description
      * set.
      */
-    @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
     @Nullable
     public String getLogoDescription() {
@@ -939,7 +931,6 @@
      *
      * @return The content view for the prompt, or null if the prompt has no content view.
      */
-    @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     @Nullable
     public PromptContentView getContentView() {
         return mPromptInfo.getContentView();
diff --git a/core/java/android/hardware/biometrics/PromptContentItem.java b/core/java/android/hardware/biometrics/PromptContentItem.java
index c47b37a..0c41782 100644
--- a/core/java/android/hardware/biometrics/PromptContentItem.java
+++ b/core/java/android/hardware/biometrics/PromptContentItem.java
@@ -16,14 +16,9 @@
 
 package android.hardware.biometrics;
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
-
 /**
  * An item shown on {@link PromptContentView}.
  */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 public interface PromptContentItem {
 }
 
diff --git a/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java b/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java
index 25e5cca..a026498 100644
--- a/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java
+++ b/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java
@@ -16,9 +16,6 @@
 
 package android.hardware.biometrics;
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -26,7 +23,6 @@
 /**
  * A list item with bulleted text shown on {@link PromptVerticalListContentView}.
  */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 public final class PromptContentItemBulletedText implements PromptContentItemParcelable {
     private final String mText;
 
diff --git a/core/java/android/hardware/biometrics/PromptContentItemParcelable.java b/core/java/android/hardware/biometrics/PromptContentItemParcelable.java
index 668912cf..1860aae 100644
--- a/core/java/android/hardware/biometrics/PromptContentItemParcelable.java
+++ b/core/java/android/hardware/biometrics/PromptContentItemParcelable.java
@@ -16,15 +16,11 @@
 
 package android.hardware.biometrics;
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
 import android.os.Parcelable;
 
 /**
  * A parcelable {@link PromptContentItem}.
  */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 sealed interface PromptContentItemParcelable extends PromptContentItem, Parcelable
         permits PromptContentItemPlainText, PromptContentItemBulletedText {
 }
diff --git a/core/java/android/hardware/biometrics/PromptContentItemPlainText.java b/core/java/android/hardware/biometrics/PromptContentItemPlainText.java
index 7919256..a5e6120 100644
--- a/core/java/android/hardware/biometrics/PromptContentItemPlainText.java
+++ b/core/java/android/hardware/biometrics/PromptContentItemPlainText.java
@@ -16,9 +16,6 @@
 
 package android.hardware.biometrics;
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -26,7 +23,6 @@
 /**
  * A list item with plain text shown on {@link PromptVerticalListContentView}.
  */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 public final class PromptContentItemPlainText implements PromptContentItemParcelable {
     private final String mText;
 
diff --git a/core/java/android/hardware/biometrics/PromptContentView.java b/core/java/android/hardware/biometrics/PromptContentView.java
index ff9313e..0836d72 100644
--- a/core/java/android/hardware/biometrics/PromptContentView.java
+++ b/core/java/android/hardware/biometrics/PromptContentView.java
@@ -16,13 +16,8 @@
 
 package android.hardware.biometrics;
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
-
 /**
  * Contains the information of the template of content view for Biometric Prompt.
  */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 public interface PromptContentView {
 }
diff --git a/core/java/android/hardware/biometrics/PromptContentViewParcelable.java b/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
index b5982d4..6e03563 100644
--- a/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
+++ b/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
@@ -16,15 +16,11 @@
 
 package android.hardware.biometrics;
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
 import android.os.Parcelable;
 
 /**
  * A parcelable {@link PromptContentView}.
  */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 sealed interface PromptContentViewParcelable extends PromptContentView, Parcelable
         permits PromptVerticalListContentView, PromptContentViewWithMoreOptionsButton {
 }
diff --git a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
index 4b9d5ce..aa0ce58 100644
--- a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
+++ b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
@@ -17,10 +17,8 @@
 package android.hardware.biometrics;
 
 import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
 
 import android.annotation.CallbackExecutor;
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -61,7 +59,6 @@
  *     .build();
  * </pre>
  */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 public final class PromptContentViewWithMoreOptionsButton implements PromptContentViewParcelable {
     private static final String TAG = "PromptContentViewWithMoreOptionsButton";
     @VisibleForTesting
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index e23ffeb..3e304e4 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -217,7 +217,7 @@
      * Returns if the PromptContentViewWithMoreOptionsButton is set.
      */
     public boolean isContentViewMoreOptionsButtonUsed() {
-        return Flags.customBiometricPrompt() && mContentView != null
+        return mContentView != null
                 && mContentView instanceof PromptContentViewWithMoreOptionsButton;
     }
 
diff --git a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
index 86006f8..2a521d1 100644
--- a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
+++ b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
@@ -16,9 +16,6 @@
 
 package android.hardware.biometrics;
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
-
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
@@ -48,7 +45,6 @@
  *     .build();
  * </pre>
  */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 public final class PromptVerticalListContentView implements PromptContentViewParcelable {
     private static final String TAG = "PromptVerticalListContentView";
     @VisibleForTesting
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 52a4898..7a23033 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -54,3 +54,20 @@
   description: "This flag is for API changes related to Identity Check"
   bug: "373424727"
 }
+
+flag {
+  name: "private_space_bp"
+  namespace: "biometrics_framework"
+  description: "Feature flag for biometric prompt improvements in private space"
+  bug: "365554098"
+}
+
+flag {
+  name: "effective_user_bp"
+  namespace: "biometrics_framework"
+  description: "Feature flag for using effective user in biometric prompt"
+  bug: "365094949"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1137e1e..16d82ca 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -775,6 +775,46 @@
             new Key<int[]>("android.colorCorrection.availableAberrationModes", int[].class);
 
     /**
+     * <p>The range of supported color temperature values for
+     * {@link CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE android.colorCorrection.colorTemperature}.</p>
+     * <p>This key lists the valid range of color temperature values for
+     * {@link CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE android.colorCorrection.colorTemperature} supported by this camera device.</p>
+     * <p>This key will be null on devices that do not support CCT mode for
+     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p>
+     * <p><b>Range of valid values:</b><br></p>
+     * <p>The minimum supported range will be [2856K,6500K]. The maximum supported
+     * range will be [1000K,40000K].</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE
+     * @see CaptureRequest#COLOR_CORRECTION_MODE
+     */
+    @PublicKey
+    @NonNull
+    @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+    public static final Key<android.util.Range<Integer>> COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE =
+            new Key<android.util.Range<Integer>>("android.colorCorrection.colorTemperatureRange", new TypeReference<android.util.Range<Integer>>() {{ }});
+
+    /**
+     * <p>List of color correction modes for {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} that are
+     * supported by this camera device.</p>
+     * <p>This key lists the valid modes for {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}. If no
+     * color correction modes are available for a device, this key will be null.</p>
+     * <p>Camera devices that have a FULL hardware level will always include at least
+     * FAST, HIGH_QUALITY, and TRANSFORM_MATRIX modes.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#COLOR_CORRECTION_MODE
+     */
+    @PublicKey
+    @NonNull
+    @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+    public static final Key<int[]> COLOR_CORRECTION_AVAILABLE_MODES =
+            new Key<int[]>("android.colorCorrection.availableModes", int[].class);
+
+    /**
      * <p>List of auto-exposure antibanding modes for {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} that are
      * supported by this camera device.</p>
      * <p>Not all of the auto-exposure anti-banding modes may be
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index acb48f3..86bbd4a 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2169,6 +2169,22 @@
      */
     public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2;
 
+    /**
+     * <p>Use
+     * {@link CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE android.colorCorrection.colorTemperature} and
+     * {@link CaptureRequest#COLOR_CORRECTION_COLOR_TINT android.colorCorrection.colorTint} to adjust the white balance based
+     * on correlated color temperature.</p>
+     * <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then
+     * CCT is ignored.</p>
+     *
+     * @see CaptureRequest#COLOR_CORRECTION_COLOR_TEMPERATURE
+     * @see CaptureRequest#COLOR_CORRECTION_COLOR_TINT
+     * @see CaptureRequest#CONTROL_AWB_MODE
+     * @see CaptureRequest#COLOR_CORRECTION_MODE
+     */
+    @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+    public static final int COLOR_CORRECTION_MODE_CCT = 3;
+
     //
     // Enumeration values for CaptureRequest#COLOR_CORRECTION_ABERRATION_MODE
     //
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index a5c5a99..8142bbe 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1086,11 +1086,17 @@
      *   <li>{@link #COLOR_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
      * </ul>
      *
+     * <p><b>Available values for this device:</b><br>
+     * Starting from API level 36, {@link CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_MODES android.colorCorrection.availableModes}
+     * can be used to check the list of supported values. Prior to API level 36,
+     * TRANSFORM_MATRIX, HIGH_QUALITY, and FAST are guaranteed to be available
+     * as valid modes on devices that support this key.</p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
      * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
+     * @see CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_MODES
      * @see CaptureRequest#COLOR_CORRECTION_GAINS
      * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
      * @see CaptureRequest#CONTROL_AWB_MODE
@@ -1195,6 +1201,60 @@
             new Key<Integer>("android.colorCorrection.aberrationMode", int.class);
 
     /**
+     * <p>Specifies the color temperature for CCT mode in Kelvin
+     * to adjust the white balance of the image.</p>
+     * <p>Sets the color temperature in Kelvin units for when
+     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is CCT to adjust the
+     * white balance of the image.</p>
+     * <p>If CCT mode is enabled without a requested color temperature,
+     * a default value will be set by the camera device. The default value can be
+     * retrieved by checking the corresponding capture result. Color temperatures
+     * requested outside the advertised {@link CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE android.colorCorrection.colorTemperatureRange}
+     * will be clamped.</p>
+     * <p><b>Units</b>: Kelvin</p>
+     * <p><b>Range of valid values:</b><br>
+     * {@link CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE android.colorCorrection.colorTemperatureRange}</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE
+     * @see CaptureRequest#COLOR_CORRECTION_MODE
+     */
+    @PublicKey
+    @NonNull
+    @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+    public static final Key<Integer> COLOR_CORRECTION_COLOR_TEMPERATURE =
+            new Key<Integer>("android.colorCorrection.colorTemperature", int.class);
+
+    /**
+     * <p>Specifies the color tint for CCT mode to adjust the white
+     * balance of the image.</p>
+     * <p>Sets the color tint for when {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}
+     * is CCT to adjust the white balance of the image.</p>
+     * <p>If CCT mode is enabled without a requested color tint,
+     * a default value will be set by the camera device. The default value can be
+     * retrieved by checking the corresponding capture result. Color tints requested
+     * outside the supported range will be clamped to the nearest limit (-50 or +50).</p>
+     * <p><b>Units</b>: D_uv defined as the distance from the Planckian locus on the CIE 1931 xy
+     * chromaticity diagram, with the range ±50 mapping to ±0.01 D_uv</p>
+     * <p><b>Range of valid values:</b><br>
+     * The supported range, -50 to +50, corresponds to a D_uv distance
+     * of ±0.01 below and above the Planckian locus. Some camera devices may have
+     * limitations to achieving the full ±0.01 D_uv range at some color temperatures
+     * (e.g., below 1500K). In these cases, the applied D_uv value may be clamped and
+     * the actual color tint will be reported in the {@link CaptureRequest#COLOR_CORRECTION_COLOR_TINT android.colorCorrection.colorTint}
+     * result.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#COLOR_CORRECTION_COLOR_TINT
+     * @see CaptureRequest#COLOR_CORRECTION_MODE
+     */
+    @PublicKey
+    @NonNull
+    @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+    public static final Key<Integer> COLOR_CORRECTION_COLOR_TINT =
+            new Key<Integer>("android.colorCorrection.colorTint", int.class);
+
+    /**
      * <p>The desired setting for the camera device's auto-exposure
      * algorithm's antibanding compensation.</p>
      * <p>Some kinds of lighting fixtures, such as some fluorescent
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index a6bdb3f..ae72ca4 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -487,11 +487,17 @@
      *   <li>{@link #COLOR_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
      * </ul>
      *
+     * <p><b>Available values for this device:</b><br>
+     * Starting from API level 36, {@link CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_MODES android.colorCorrection.availableModes}
+     * can be used to check the list of supported values. Prior to API level 36,
+     * TRANSFORM_MATRIX, HIGH_QUALITY, and FAST are guaranteed to be available
+     * as valid modes on devices that support this key.</p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
      * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
+     * @see CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_MODES
      * @see CaptureRequest#COLOR_CORRECTION_GAINS
      * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
      * @see CaptureRequest#CONTROL_AWB_MODE
@@ -596,6 +602,60 @@
             new Key<Integer>("android.colorCorrection.aberrationMode", int.class);
 
     /**
+     * <p>Specifies the color temperature for CCT mode in Kelvin
+     * to adjust the white balance of the image.</p>
+     * <p>Sets the color temperature in Kelvin units for when
+     * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is CCT to adjust the
+     * white balance of the image.</p>
+     * <p>If CCT mode is enabled without a requested color temperature,
+     * a default value will be set by the camera device. The default value can be
+     * retrieved by checking the corresponding capture result. Color temperatures
+     * requested outside the advertised {@link CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE android.colorCorrection.colorTemperatureRange}
+     * will be clamped.</p>
+     * <p><b>Units</b>: Kelvin</p>
+     * <p><b>Range of valid values:</b><br>
+     * {@link CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE android.colorCorrection.colorTemperatureRange}</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE
+     * @see CaptureRequest#COLOR_CORRECTION_MODE
+     */
+    @PublicKey
+    @NonNull
+    @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+    public static final Key<Integer> COLOR_CORRECTION_COLOR_TEMPERATURE =
+            new Key<Integer>("android.colorCorrection.colorTemperature", int.class);
+
+    /**
+     * <p>Specifies the color tint for CCT mode to adjust the white
+     * balance of the image.</p>
+     * <p>Sets the color tint for when {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}
+     * is CCT to adjust the white balance of the image.</p>
+     * <p>If CCT mode is enabled without a requested color tint,
+     * a default value will be set by the camera device. The default value can be
+     * retrieved by checking the corresponding capture result. Color tints requested
+     * outside the supported range will be clamped to the nearest limit (-50 or +50).</p>
+     * <p><b>Units</b>: D_uv defined as the distance from the Planckian locus on the CIE 1931 xy
+     * chromaticity diagram, with the range ±50 mapping to ±0.01 D_uv</p>
+     * <p><b>Range of valid values:</b><br>
+     * The supported range, -50 to +50, corresponds to a D_uv distance
+     * of ±0.01 below and above the Planckian locus. Some camera devices may have
+     * limitations to achieving the full ±0.01 D_uv range at some color temperatures
+     * (e.g., below 1500K). In these cases, the applied D_uv value may be clamped and
+     * the actual color tint will be reported in the {@link CaptureRequest#COLOR_CORRECTION_COLOR_TINT android.colorCorrection.colorTint}
+     * result.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#COLOR_CORRECTION_COLOR_TINT
+     * @see CaptureRequest#COLOR_CORRECTION_MODE
+     */
+    @PublicKey
+    @NonNull
+    @FlaggedApi(Flags.FLAG_COLOR_TEMPERATURE)
+    public static final Key<Integer> COLOR_CORRECTION_COLOR_TINT =
+            new Key<Integer>("android.colorCorrection.colorTint", int.class);
+
+    /**
      * <p>The desired setting for the camera device's auto-exposure
      * algorithm's antibanding compensation.</p>
      * <p>Some kinds of lighting fixtures, such as some fluorescent
diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
index 116928b..8ede7f3 100644
--- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -224,9 +224,8 @@
      * @see
      * android.hardware.camera2.params.MultiResolutionStreamConfigurationMap
      *
-     * @hide
      */
-    @FlaggedApi(Flags.FLAG_MULTIRESOLUTION_IMAGEREADER_USAGE_CONFIG)
+    @FlaggedApi(Flags.FLAG_MULTIRESOLUTION_IMAGEREADER_USAGE_PUBLIC)
     public MultiResolutionImageReader(
             @NonNull Collection<MultiResolutionStreamInfo> streams,
             @Format             int format,
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 22dbf5b..d38be9b 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1457,7 +1457,7 @@
     /**
      * Set the mirroring mode for a surface belonging to this OutputConfiguration
      *
-     * <p>This function is identical to {@link #setMirroMode(int)} if {@code surface} is
+     * <p>This function is identical to {@link #setMirrorMode(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
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b0ea92d..a81bcbc 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -580,7 +580,7 @@
             EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface EventsMask {}
+    public @interface EventFlag {}
 
     /**
      * Event type for when a new display is added.
@@ -774,7 +774,7 @@
      * @param listener The listener to register.
      * @param handler The handler on which the listener should be invoked, or null
      * if the listener should be invoked on the calling thread's looper.
-     * @param eventsMask A bitmask of the event types for which this listener is subscribed.
+     * @param eventFlagsMask A bitmask of the event types for which this listener is subscribed.
      *
      * @see #EVENT_FLAG_DISPLAY_ADDED
      * @see #EVENT_FLAG_DISPLAY_CHANGED
@@ -786,8 +786,8 @@
      * @hide
      */
     public void registerDisplayListener(@NonNull DisplayListener listener,
-            @Nullable Handler handler, @EventsMask long eventsMask) {
-        mGlobal.registerDisplayListener(listener, handler, eventsMask,
+            @Nullable Handler handler, @EventFlag long eventFlagsMask) {
+        mGlobal.registerDisplayListener(listener, handler, eventFlagsMask,
                 ActivityThread.currentPackageName());
     }
 
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 6affd12..3c6841c 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -17,7 +17,7 @@
 package android.hardware.display;
 
 
-import static android.hardware.display.DisplayManager.EventsMask;
+import static android.hardware.display.DisplayManager.EventFlag;
 import static android.view.Display.HdrCapabilities.HdrType;
 
 import android.Manifest;
@@ -130,7 +130,7 @@
     private final IDisplayManager mDm;
 
     private DisplayManagerCallback mCallback;
-    private @EventsMask long mRegisteredEventsMask = 0;
+    private @EventFlag long mRegisteredEventFlagsMask = 0;
     private final CopyOnWriteArrayList<DisplayListenerDelegate> mDisplayListeners =
             new CopyOnWriteArrayList<>();
 
@@ -346,10 +346,11 @@
      * @param packageName of the calling package.
      */
     public void registerDisplayListener(@NonNull DisplayListener listener,
-            @Nullable Handler handler, @EventsMask long eventsMask, String packageName) {
+            @Nullable Handler handler, @EventFlag long eventFlagsMask,
+            String packageName) {
         Looper looper = getLooperForHandler(handler);
         Handler springBoard = new Handler(looper);
-        registerDisplayListener(listener, new HandlerExecutor(springBoard), eventsMask,
+        registerDisplayListener(listener, new HandlerExecutor(springBoard), eventFlagsMask,
                 packageName);
     }
 
@@ -358,32 +359,32 @@
      *
      * @param listener The listener that will be called when display changes occur.
      * @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
-     * @param eventsMask Mask of events to be listened to.
+     * @param eventFlagsMask Flag of events to be listened to.
      * @param packageName of the calling package.
      */
     public void registerDisplayListener(@NonNull DisplayListener listener,
-            @NonNull Executor executor, @EventsMask long eventsMask, String packageName) {
+            @NonNull Executor executor, @EventFlag long eventFlagsMask, String packageName) {
         if (listener == null) {
             throw new IllegalArgumentException("listener must not be null");
         }
 
-        if (eventsMask == 0) {
+        if (eventFlagsMask == 0) {
             throw new IllegalArgumentException("The set of events to listen to must not be empty.");
         }
 
         if (extraLogging()) {
             Slog.i(TAG, "Registering Display Listener: "
-                    + Long.toBinaryString(eventsMask) + ", packageName: " + packageName);
+                    + Long.toBinaryString(eventFlagsMask) + ", packageName: " + packageName);
         }
 
         synchronized (mLock) {
             int index = findDisplayListenerLocked(listener);
             if (index < 0) {
-                mDisplayListeners.add(new DisplayListenerDelegate(listener, executor, eventsMask,
-                        packageName));
+                mDisplayListeners.add(new DisplayListenerDelegate(listener, executor,
+                        eventFlagsMask, packageName));
                 registerCallbackIfNeededLocked();
             } else {
-                mDisplayListeners.get(index).setEventsMask(eventsMask);
+                mDisplayListeners.get(index).setEventFlagsMask(eventFlagsMask);
             }
             updateCallbackIfNeededLocked();
             maybeLogAllDisplayListeners();
@@ -455,12 +456,12 @@
         return -1;
     }
 
-    @EventsMask
-    private int calculateEventsMaskLocked() {
+    @EventFlag
+    private int calculateEventFlagsMaskLocked() {
         int mask = 0;
         final int numListeners = mDisplayListeners.size();
         for (int i = 0; i < numListeners; i++) {
-            mask |= mDisplayListeners.get(i).mEventsMask;
+            mask |= mDisplayListeners.get(i).mEventFlagsMask;
         }
         if (mDispatchNativeCallbacks) {
             mask |= DisplayManager.EVENT_FLAG_DISPLAY_ADDED
@@ -478,14 +479,14 @@
     }
 
     private void updateCallbackIfNeededLocked() {
-        int mask = calculateEventsMaskLocked();
+        int mask = calculateEventFlagsMaskLocked();
         if (DEBUG) {
-            Log.d(TAG, "Mask for listener: " + mask);
+            Log.d(TAG, "Flag for listener: " + mask);
         }
-        if (mask != mRegisteredEventsMask) {
+        if (mask != mRegisteredEventFlagsMask) {
             try {
                 mDm.registerCallbackWithEventMask(mCallback, mask);
-                mRegisteredEventsMask = mask;
+                mRegisteredEventFlagsMask = mask;
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
@@ -1276,7 +1277,7 @@
 
     private static final class DisplayListenerDelegate {
         public final DisplayListener mListener;
-        public volatile long mEventsMask;
+        public volatile long mEventFlagsMask;
 
         private final DisplayInfo mDisplayInfo = new DisplayInfo();
         private final Executor mExecutor;
@@ -1284,10 +1285,10 @@
         private final String mPackageName;
 
         DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
-                @EventsMask long eventsMask, String packageName) {
+                @EventFlag long eventFlag, String packageName) {
             mExecutor = executor;
             mListener = listener;
-            mEventsMask = eventsMask;
+            mEventFlagsMask = eventFlag;
             mPackageName = packageName;
         }
 
@@ -1309,16 +1310,16 @@
             mGenerationId.incrementAndGet();
         }
 
-        void setEventsMask(@EventsMask long newEventsMask) {
-            mEventsMask = newEventsMask;
+        void setEventFlagsMask(@EventFlag long newEventsFlag) {
+            mEventFlagsMask = newEventsFlag;
         }
 
-        private void handleDisplayEventInner(int displayId, @DisplayEvent int event,
+        private void handleDisplayEventInner(int displayId, @DisplayEvent int eventFlagsMask,
                 @Nullable DisplayInfo info, boolean forceUpdate) {
             if (extraLogging()) {
-                Slog.i(TAG, "DLD(" + eventToString(event)
+                Slog.i(TAG, "DLD(" + eventToString(eventFlagsMask)
                         + ", display=" + displayId
-                        + ", mEventsMask=" + Long.toBinaryString(mEventsMask)
+                        + ", mEventsFlagMask=" + Long.toBinaryString(mEventFlagsMask)
                         + ", mPackageName=" + mPackageName
                         + ", displayInfo=" + info
                         + ", listener=" + mListener.getClass() + ")");
@@ -1326,18 +1327,18 @@
             if (DEBUG) {
                 Trace.beginSection(
                         TextUtils.trimToSize(
-                                "DLD(" + eventToString(event)
+                                "DLD(" + eventToString(eventFlagsMask)
                                 + ", display=" + displayId
                                 + ", listener=" + mListener.getClass() + ")", 127));
             }
-            switch (event) {
+            switch (eventFlagsMask) {
                 case EVENT_DISPLAY_ADDED:
-                    if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
+                    if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
                         mListener.onDisplayAdded(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_CHANGED:
-                    if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
+                    if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
                         if (info != null && (forceUpdate || !info.equals(mDisplayInfo))) {
                             if (extraLogging()) {
                                 Slog.i(TAG, "Sending onDisplayChanged: Display Changed. Info: "
@@ -1349,27 +1350,29 @@
                     }
                     break;
                 case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
-                    if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) {
+                    if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0) {
                         mListener.onDisplayChanged(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_REMOVED:
-                    if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) {
+                    if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0) {
                         mListener.onDisplayRemoved(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
-                    if ((mEventsMask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) {
+                    if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) {
                         mListener.onDisplayChanged(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_CONNECTED:
-                    if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+                    if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
+                            != 0) {
                         mListener.onDisplayConnected(displayId);
                     }
                     break;
                 case EVENT_DISPLAY_DISCONNECTED:
-                    if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+                    if ((mEventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
+                            != 0) {
                         mListener.onDisplayDisconnected(displayId);
                     }
                     break;
@@ -1381,7 +1384,7 @@
 
         @Override
         public String toString() {
-            return "mask: {" + mEventsMask + "}, for " + mListener.getClass();
+            return "mEventFlagsMask: {" + mEventFlagsMask + "}, for " + mListener.getClass();
         }
     }
 
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 75ffcc3..399184c 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -491,10 +491,16 @@
         public static final int POLICY_DIM = 2;
         // Policy: Make the screen bright as usual.
         public static final int POLICY_BRIGHT = 3;
+        // The maximum policy constant. Useful for iterating through all constants in tests.
+        public static final int POLICY_MAX = POLICY_BRIGHT;
 
         // The basic overall policy to apply: off, doze, dim or bright.
         public int policy;
 
+        // The reason behind the current policy.
+        @Display.StateReason
+        public int policyReason;
+
         // If true, the proximity sensor overrides the screen state when an object is
         // nearby, turning it off temporarily until the object is moved away.
         public boolean useProximitySensor;
@@ -541,6 +547,7 @@
 
         public DisplayPowerRequest() {
             policy = POLICY_BRIGHT;
+            policyReason = Display.STATE_REASON_DEFAULT_POLICY;
             useProximitySensor = false;
             screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             screenAutoBrightnessAdjustmentOverride = Float.NaN;
@@ -561,6 +568,7 @@
 
         public void copyFrom(DisplayPowerRequest other) {
             policy = other.policy;
+            policyReason = other.policyReason;
             useProximitySensor = other.useProximitySensor;
             screenBrightnessOverride = other.screenBrightnessOverride;
             screenBrightnessOverrideTag = other.screenBrightnessOverrideTag;
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index b0994e6..49944c7 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -60,6 +60,7 @@
     private final float mRequestedRefreshRate;
     private final boolean mIsHomeSupported;
     private final DisplayCutout mDisplayCutout;
+    private final boolean mIgnoreActivitySizeRestrictions;
 
     private VirtualDisplayConfig(
             @NonNull String name,
@@ -74,7 +75,8 @@
             @NonNull ArraySet<String> displayCategories,
             float requestedRefreshRate,
             boolean isHomeSupported,
-            @Nullable DisplayCutout displayCutout) {
+            @Nullable DisplayCutout displayCutout,
+            boolean ignoreActivitySizeRestrictions) {
         mName = name;
         mWidth = width;
         mHeight = height;
@@ -88,6 +90,7 @@
         mRequestedRefreshRate = requestedRefreshRate;
         mIsHomeSupported = isHomeSupported;
         mDisplayCutout = displayCutout;
+        mIgnoreActivitySizeRestrictions = ignoreActivitySizeRestrictions;
     }
 
     /**
@@ -193,6 +196,20 @@
     }
 
     /**
+     * Whether this virtual display ignores fixed orientation, aspect ratio and resizability
+     * of apps.
+     *
+     * @see Builder#setIgnoreActivitySizeRestrictions(boolean)
+     * @hide
+     */
+    @FlaggedApi(com.android.window.flags.Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+    @SystemApi
+    public boolean isIgnoreActivitySizeRestrictions() {
+        return mIgnoreActivitySizeRestrictions
+                && com.android.window.flags.Flags.vdmForceAppUniversalResizableApi();
+    }
+
+    /**
      * Returns the display categories.
      *
      * @see Builder#setDisplayCategories
@@ -227,6 +244,7 @@
         dest.writeFloat(mRequestedRefreshRate);
         dest.writeBoolean(mIsHomeSupported);
         DisplayCutout.ParcelableWrapper.writeCutoutToParcel(mDisplayCutout, dest, flags);
+        dest.writeBoolean(mIgnoreActivitySizeRestrictions);
     }
 
     @Override
@@ -253,6 +271,7 @@
                 && Objects.equals(mDisplayCategories, that.mDisplayCategories)
                 && mRequestedRefreshRate == that.mRequestedRefreshRate
                 && mIsHomeSupported == that.mIsHomeSupported
+                && mIgnoreActivitySizeRestrictions == that.mIgnoreActivitySizeRestrictions
                 && Objects.equals(mDisplayCutout, that.mDisplayCutout);
     }
 
@@ -261,7 +280,8 @@
         int hashCode = Objects.hash(
                 mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
                 mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
-                mRequestedRefreshRate, mIsHomeSupported, mDisplayCutout);
+                mRequestedRefreshRate, mIsHomeSupported, mDisplayCutout,
+                mIgnoreActivitySizeRestrictions);
         return hashCode;
     }
 
@@ -282,6 +302,7 @@
                 + " mRequestedRefreshRate=" + mRequestedRefreshRate
                 + " mIsHomeSupported=" + mIsHomeSupported
                 + " mDisplayCutout=" + mDisplayCutout
+                + " mIgnoreActivitySizeRestrictions=" + mIgnoreActivitySizeRestrictions
                 + ")";
     }
 
@@ -299,6 +320,7 @@
         mRequestedRefreshRate = in.readFloat();
         mIsHomeSupported = in.readBoolean();
         mDisplayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(in);
+        mIgnoreActivitySizeRestrictions = in.readBoolean();
     }
 
     @NonNull
@@ -332,6 +354,7 @@
         private float mRequestedRefreshRate = 0.0f;
         private boolean mIsHomeSupported = false;
         private DisplayCutout mDisplayCutout = null;
+        private boolean mIgnoreActivitySizeRestrictions = false;
 
         /**
          * Creates a new Builder.
@@ -506,6 +529,24 @@
         }
 
         /**
+         * Sets whether this display ignores fixed orientation, aspect ratio and resizability
+         * of apps.
+         *
+         * <p>Note: setting to {@code true} requires the display to have
+         * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED}. If this is false, this property
+         * is ignored.</p>
+         *
+         * @hide
+         */
+        @FlaggedApi(com.android.window.flags.Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+        @SystemApi
+        @NonNull
+        public Builder setIgnoreActivitySizeRestrictions(boolean enabled) {
+            mIgnoreActivitySizeRestrictions = enabled;
+            return this;
+        }
+
+        /**
          * Builds the {@link VirtualDisplayConfig} instance.
          */
         @NonNull
@@ -523,7 +564,8 @@
                     mDisplayCategories,
                     mRequestedRefreshRate,
                     mIsHomeSupported,
-                    mDisplayCutout);
+                    mDisplayCutout,
+                    mIgnoreActivitySizeRestrictions);
         }
     }
 }
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/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 ee1a6ab..9d42b67 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -109,6 +109,14 @@
     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 KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW = 68;
+    public static final int KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW = 69;
+    public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 70;
+    public static final int KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE = 71;
 
     public static final int FLAG_CANCELLED = 1;
 
@@ -187,6 +195,14 @@
             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,
+            KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+            KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+            KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+            KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KeyGestureType {
@@ -533,6 +549,14 @@
                 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;
+            case KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SNAP_LEFT_FREEFORM_WINDOW;
+            case KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SNAP_RIGHT_FREEFORM_WINDOW;
+            case KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MAXIMIZE_FREEFORM_WINDOW;
+            case KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE:
+                return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RESTORE_FREEFORM_WINDOW_SIZE;
             default:
                 return LOG_EVENT_UNSPECIFIED;
         }
@@ -733,6 +757,22 @@
                 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";
+            case KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW:
+                return "KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW";
+            case KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW:
+                return "KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW";
+            case KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW:
+                return "KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW";
+            case KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE:
+                return "KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE";
             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 1206855..71c91e9 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -1,7 +1,10 @@
 package: "com.android.hardware.input"
 container: "system"
 
-# Project link: https://gantry.corp.google.com/projects/android_platform_input_native/changes
+# Project link: https://gantry.corp.google.com/projects/android_platform_input/changes
+
+# NOTE: the input_native namespace is deprecated. New flags should be added to the input namespace
+# instead.
 
 flag {
     namespace: "input_native"
@@ -143,6 +146,13 @@
 }
 
 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."
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 41f344a..92608d0 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -21,6 +21,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.LongDef;
 import android.annotation.NonNull;
@@ -37,6 +38,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.usb.flags.Flags;
 import android.hardware.usb.gadget.GadgetFunction;
 import android.hardware.usb.gadget.UsbSpeed;
 import android.os.Binder;
@@ -509,7 +511,8 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_EXPOSE_USB_SPEED_SYSTEM_API)
+    @SystemApi
     public static final int USB_DATA_TRANSFER_RATE_UNKNOWN = -1;
 
     /**
@@ -517,7 +520,8 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_EXPOSE_USB_SPEED_SYSTEM_API)
+    @SystemApi
     public static final int USB_DATA_TRANSFER_RATE_LOW_SPEED = 2;
 
     /**
@@ -525,7 +529,8 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_EXPOSE_USB_SPEED_SYSTEM_API)
+    @SystemApi
     public static final int USB_DATA_TRANSFER_RATE_FULL_SPEED = 12;
 
     /**
@@ -533,7 +538,8 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_EXPOSE_USB_SPEED_SYSTEM_API)
+    @SystemApi
     public static final int USB_DATA_TRANSFER_RATE_HIGH_SPEED = 480;
 
     /**
@@ -541,7 +547,8 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_EXPOSE_USB_SPEED_SYSTEM_API)
+    @SystemApi
     public static final int USB_DATA_TRANSFER_RATE_5G = 5 * 1024;
 
     /**
@@ -549,7 +556,8 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_EXPOSE_USB_SPEED_SYSTEM_API)
+    @SystemApi
     public static final int USB_DATA_TRANSFER_RATE_10G = 10 * 1024;
 
     /**
@@ -557,7 +565,8 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_EXPOSE_USB_SPEED_SYSTEM_API)
+    @SystemApi
     public static final int USB_DATA_TRANSFER_RATE_20G = 20 * 1024;
 
     /**
@@ -565,7 +574,8 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_EXPOSE_USB_SPEED_SYSTEM_API)
+    @SystemApi
     public static final int USB_DATA_TRANSFER_RATE_40G = 40 * 1024;
 
     /**
@@ -1292,7 +1302,8 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @FlaggedApi(Flags.FLAG_EXPOSE_USB_SPEED_SYSTEM_API)
+    @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_USB)
     public int getUsbBandwidthMbps() {
         int usbSpeed;
diff --git a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
index 40e5ffb..3b7a9e9 100644
--- a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
@@ -24,3 +24,10 @@
     description: "Feature flag to enable interface name as a parameter for device filter"
     bug: "312828160"
 }
+
+flag {
+    name: "expose_usb_speed_system_api"
+    namespace: "usb"
+    description: "Feature flag to enable exposing usb speed system api"
+    bug: "373653182"
+}
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/flags.aconfig b/core/java/android/net/flags.aconfig
index 48eb968..f7dc790 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -5,13 +5,6 @@
 # Flags used for module APIs must be in aconfig files under each modules
 
 flag {
-  name: "ipsec_transform_state"
-  namespace: "core_networking_ipsec"
-  description: "The flag controls the access for getIpSecTransformState and IpSecTransformState"
-  bug: "308011229"
-}
-
-flag {
     name: "powered_off_finding_platform"
     namespace: "nearby"
     description: "Controls whether the Powered Off Finding feature is enabled"
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/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index 5b30624..1adefe5 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -10,8 +10,26 @@
 }
 
 flag {
+     name: "mainline_vcn_module_api"
+     namespace: "vcn"
+     description: "Expose APIs from VCN for mainline migration"
+     is_exported: true
+     bug: "376339506"
+}
+
+flag {
     name: "safe_mode_timeout_config"
     namespace: "vcn"
     description: "Feature flag for adjustable safe mode timeout"
     bug: "317406085"
+}
+
+flag {
+    name: "fix_config_garbage_collection"
+    namespace: "vcn"
+    description: "Handle race condition in subscription change"
+    bug: "370862489"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
new file mode 100644
index 0000000..66f4198
--- /dev/null
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -0,0 +1,2516 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Process;
+import android.os.UserHandle;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodRedirect;
+import android.ravenwood.annotation.RavenwoodRedirectionClass;
+import android.util.Log;
+import android.util.Printer;
+import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
+
+import dalvik.annotation.optimization.NeverCompile;
+
+import java.io.FileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Low-level class holding the list of messages to be dispatched by a
+ * {@link Looper}.  Messages are not added directly to a MessageQueue,
+ * but rather through {@link Handler} objects associated with the Looper.
+ *
+ * <p>You can retrieve the MessageQueue for the current thread with
+ * {@link Looper#myQueue() Looper.myQueue()}.
+ */
+@RavenwoodKeepWholeClass
+@RavenwoodRedirectionClass("MessageQueue_host")
+public final class MessageQueue {
+    private static final String TAG_L = "LegacyMessageQueue";
+    private static final String TAG_C = "ConcurrentMessageQueue";
+    private static final boolean DEBUG = false;
+    private static final boolean TRACE = false;
+
+    // True if the message queue can be quit.
+    @UnsupportedAppUsage
+    private final boolean mQuitAllowed;
+
+    @UnsupportedAppUsage
+    @SuppressWarnings("unused")
+    private long mPtr; // used by native code
+
+    @UnsupportedAppUsage
+    Message mMessages;
+    private Message mLast;
+    @UnsupportedAppUsage
+    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
+    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
+    private IdleHandler[] mPendingIdleHandlers;
+    private boolean mQuitting;
+
+    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
+    private boolean mBlocked;
+
+    // Tracks the number of async message. We use this in enqueueMessage() to avoid searching the
+    // queue for async messages when inserting a message at the tail.
+    private int mAsyncMessageCount;
+
+    /*
+     * Select between two implementations of message queue. The legacy implementation is used
+     * by default as it provides maximum compatibility with applications and tests that
+     * reach into MessageQueue via the mMessages field. The concurrent implemmentation is used for
+     * system processes and provides a higher level of concurrency and higher enqueue throughput
+     * than the legacy implementation.
+     */
+    private static boolean sUseConcurrent;
+
+    private static boolean sUseConcurrentInitialized = false;
+
+    @RavenwoodRedirect
+    private native static long nativeInit();
+    @RavenwoodRedirect
+    private native static void nativeDestroy(long ptr);
+    @UnsupportedAppUsage
+    @RavenwoodRedirect
+    private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
+    @RavenwoodRedirect
+    private native static void nativeWake(long ptr);
+    @RavenwoodRedirect
+    private native static boolean nativeIsPolling(long ptr);
+    @RavenwoodRedirect
+    private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
+
+    MessageQueue(boolean quitAllowed) {
+        if (!sUseConcurrentInitialized) {
+            sUseConcurrent = UserHandle.isCore(Process.myUid());
+            sUseConcurrentInitialized = true;
+        }
+        mQuitAllowed = quitAllowed;
+        mPtr = nativeInit();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            dispose();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    // Disposes of the underlying message queue.
+    // Must only be called on the looper thread or the finalizer.
+    private void dispose() {
+        if (mPtr != 0) {
+            nativeDestroy(mPtr);
+            mPtr = 0;
+        }
+    }
+
+    private static final class MatchDeliverableMessages extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.when <= when) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchDeliverableMessages mMatchDeliverableMessages =
+            new MatchDeliverableMessages();
+    /**
+     * Returns true if the looper has no pending messages which are due to be processed.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @return True if the looper is idle.
+     */
+    public boolean isIdle() {
+        if (sUseConcurrent) {
+            final long now = SystemClock.uptimeMillis();
+
+            if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) {
+                return false;
+            }
+
+            MessageNode msgNode = null;
+            MessageNode asyncMsgNode = null;
+
+            if (!mPriorityQueue.isEmpty()) {
+                try {
+                    msgNode = mPriorityQueue.first();
+                } catch (NoSuchElementException e) { }
+            }
+
+            if (!mAsyncPriorityQueue.isEmpty()) {
+                try {
+                    asyncMsgNode = mAsyncPriorityQueue.first();
+                } catch (NoSuchElementException e) { }
+            }
+
+            if ((msgNode != null && msgNode.getWhen() <= now)
+                    || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) {
+                return false;
+            }
+
+            return true;
+        } else {
+            synchronized (this) {
+                final long now = SystemClock.uptimeMillis();
+                return mMessages == null || now < mMessages.when;
+            }
+        }
+    }
+
+    /**
+     * Add a new {@link IdleHandler} to this message queue.  This may be
+     * removed automatically for you by returning false from
+     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
+     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @param handler The IdleHandler to be added.
+     */
+    public void addIdleHandler(@NonNull IdleHandler handler) {
+        if (handler == null) {
+            throw new NullPointerException("Can't add a null IdleHandler");
+        }
+        if (sUseConcurrent) {
+            synchronized (mIdleHandlersLock) {
+                mIdleHandlers.add(handler);
+            }
+        } else {
+            synchronized (this) {
+                mIdleHandlers.add(handler);
+            }
+        }
+    }
+
+    /**
+     * Remove an {@link IdleHandler} from the queue that was previously added
+     * with {@link #addIdleHandler}.  If the given object is not currently
+     * in the idle list, nothing is done.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @param handler The IdleHandler to be removed.
+     */
+    public void removeIdleHandler(@NonNull IdleHandler handler) {
+        if (sUseConcurrent) {
+            synchronized (mIdleHandlersLock) {
+                mIdleHandlers.remove(handler);
+            }
+        } else {
+            synchronized (this) {
+                mIdleHandlers.remove(handler);
+            }
+        }
+    }
+
+    /**
+     * Returns whether this looper's thread is currently polling for more work to do.
+     * This is a good signal that the loop is still alive rather than being stuck
+     * handling a callback.  Note that this method is intrinsically racy, since the
+     * state of the loop can change before you get the result back.
+     *
+     * <p>This method is safe to call from any thread.
+     *
+     * @return True if the looper is currently polling for events.
+     * @hide
+     */
+    public boolean isPolling() {
+        if (sUseConcurrent) {
+            // If the loop is quitting then it must not be idling.
+            // We can assume mPtr != 0 when sQuitting is false.
+            return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr);
+        } else {
+            synchronized (this) {
+                return isPollingLocked();
+            }
+        }
+    }
+
+    private boolean isPollingLocked() {
+        // If the loop is quitting then it must not be idling.
+        // We can assume mPtr != 0 when mQuitting is false.
+        return !mQuitting && nativeIsPolling(mPtr);
+    }
+
+    /**
+     * Adds a file descriptor listener to receive notification when file descriptor
+     * related events occur.
+     * <p>
+     * If the file descriptor has already been registered, the specified events
+     * and listener will replace any that were previously associated with it.
+     * It is not possible to set more than one listener per file descriptor.
+     * </p><p>
+     * It is important to always unregister the listener when the file descriptor
+     * is no longer of use.
+     * </p>
+     *
+     * @param fd The file descriptor for which a listener will be registered.
+     * @param events The set of events to receive: a combination of the
+     * {@link OnFileDescriptorEventListener#EVENT_INPUT},
+     * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and
+     * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks.  If the requested
+     * set of events is zero, then the listener is unregistered.
+     * @param listener The listener to invoke when file descriptor events occur.
+     *
+     * @see OnFileDescriptorEventListener
+     * @see #removeOnFileDescriptorEventListener
+     */
+    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+    public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
+            @OnFileDescriptorEventListener.Events int events,
+            @NonNull OnFileDescriptorEventListener listener) {
+        if (fd == null) {
+            throw new IllegalArgumentException("fd must not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+
+        if (sUseConcurrent) {
+            synchronized (mFileDescriptorRecordsLock) {
+                updateOnFileDescriptorEventListenerLocked(fd, events, listener);
+            }
+        } else {
+            synchronized (this) {
+                updateOnFileDescriptorEventListenerLocked(fd, events, listener);
+            }
+        }
+    }
+
+    /**
+     * Removes a file descriptor listener.
+     * <p>
+     * This method does nothing if no listener has been registered for the
+     * specified file descriptor.
+     * </p>
+     *
+     * @param fd The file descriptor whose listener will be unregistered.
+     *
+     * @see OnFileDescriptorEventListener
+     * @see #addOnFileDescriptorEventListener
+     */
+    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+    public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
+        if (fd == null) {
+            throw new IllegalArgumentException("fd must not be null");
+        }
+        if (sUseConcurrent) {
+            synchronized (mFileDescriptorRecordsLock) {
+                updateOnFileDescriptorEventListenerLocked(fd, 0, null);
+            }
+        } else {
+            synchronized (this) {
+                updateOnFileDescriptorEventListenerLocked(fd, 0, null);
+            }
+        }
+    }
+
+    @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
+    private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
+            OnFileDescriptorEventListener listener) {
+        final int fdNum = fd.getInt$();
+
+        int index = -1;
+        FileDescriptorRecord record = null;
+        if (mFileDescriptorRecords != null) {
+            index = mFileDescriptorRecords.indexOfKey(fdNum);
+            if (index >= 0) {
+                record = mFileDescriptorRecords.valueAt(index);
+                if (record != null && record.mEvents == events) {
+                    return;
+                }
+            }
+        }
+
+        if (events != 0) {
+            events |= OnFileDescriptorEventListener.EVENT_ERROR;
+            if (record == null) {
+                if (mFileDescriptorRecords == null) {
+                    mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
+                }
+                record = new FileDescriptorRecord(fd, events, listener);
+                mFileDescriptorRecords.put(fdNum, record);
+            } else {
+                record.mListener = listener;
+                record.mEvents = events;
+                record.mSeq += 1;
+            }
+            nativeSetFileDescriptorEvents(mPtr, fdNum, events);
+        } else if (record != null) {
+            record.mEvents = 0;
+            mFileDescriptorRecords.removeAt(index);
+            nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
+        }
+    }
+
+    // Called from native code.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    private int dispatchEvents(int fd, int events) {
+        // Get the file descriptor record and any state that might change.
+        final FileDescriptorRecord record;
+        final int oldWatchedEvents;
+        final OnFileDescriptorEventListener listener;
+        final int seq;
+        if (sUseConcurrent) {
+            synchronized (mFileDescriptorRecordsLock) {
+                record = mFileDescriptorRecords.get(fd);
+                if (record == null) {
+                    return 0; // spurious, no listener registered
+                }
+
+                oldWatchedEvents = record.mEvents;
+                events &= oldWatchedEvents; // filter events based on current watched set
+                if (events == 0) {
+                    return oldWatchedEvents; // spurious, watched events changed
+                }
+
+                listener = record.mListener;
+                seq = record.mSeq;
+            }
+        } else {
+            synchronized (this) {
+                record = mFileDescriptorRecords.get(fd);
+                if (record == null) {
+                    return 0; // spurious, no listener registered
+                }
+
+                oldWatchedEvents = record.mEvents;
+                events &= oldWatchedEvents; // filter events based on current watched set
+                if (events == 0) {
+                    return oldWatchedEvents; // spurious, watched events changed
+                }
+
+                listener = record.mListener;
+                seq = record.mSeq;
+            }
+        }
+        // Invoke the listener outside of the lock.
+        int newWatchedEvents = listener.onFileDescriptorEvents(
+                record.mDescriptor, events);
+        if (newWatchedEvents != 0) {
+            newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
+        }
+
+        // Update the file descriptor record if the listener changed the set of
+        // events to watch and the listener itself hasn't been updated since.
+        if (newWatchedEvents != oldWatchedEvents) {
+            synchronized (this) {
+                int index = mFileDescriptorRecords.indexOfKey(fd);
+                if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
+                        && record.mSeq == seq) {
+                    record.mEvents = newWatchedEvents;
+                    if (newWatchedEvents == 0) {
+                        mFileDescriptorRecords.removeAt(index);
+                    }
+                }
+            }
+        }
+
+        // Return the new set of events to watch for native code to take care of.
+        return newWatchedEvents;
+    }
+
+    private static final AtomicLong mMessagesDelivered = new AtomicLong();
+
+    /* This is only read/written from the Looper thread. For use with Concurrent MQ */
+    private int mNextPollTimeoutMillis;
+    private boolean mMessageDirectlyQueued;
+    private Message nextMessage() {
+        int i = 0;
+
+        while (true) {
+            if (DEBUG) {
+                Log.d(TAG_C, "nextMessage loop #" + i);
+                i++;
+            }
+
+            mDrainingLock.lock();
+            mNextIsDrainingStack = true;
+            mDrainingLock.unlock();
+
+            /*
+             * Set our state to active, drain any items from the stack into our priority queues
+             */
+            StackNode oldTop;
+            oldTop = swapAndSetStackStateActive();
+            drainStack(oldTop);
+
+            mDrainingLock.lock();
+            mNextIsDrainingStack = false;
+            mDrainCompleted.signalAll();
+            mDrainingLock.unlock();
+
+            /*
+             * The objective of this next block of code is to:
+             *  - find a message to return (if any is ready)
+             *  - find a next message we would like to return, after scheduling.
+             *     - we make our scheduling decision based on this next message (if it exists).
+             *
+             * We have two queues to juggle and the presence of barriers throws an additional
+             * wrench into our plans.
+             *
+             * The last wrinkle is that remove() may delete items from underneath us. If we hit
+             * that case, we simply restart the loop.
+             */
+
+            /* Get the first node from each queue */
+            Iterator<MessageNode> queueIter = mPriorityQueue.iterator();
+            MessageNode msgNode = iterateNext(queueIter);
+            Iterator<MessageNode> asyncQueueIter = mAsyncPriorityQueue.iterator();
+            MessageNode asyncMsgNode = iterateNext(asyncQueueIter);
+
+            if (DEBUG) {
+                if (msgNode != null) {
+                    Message msg = msgNode.mMessage;
+                    Log.d(TAG_C, "Next found node what: " + msg.what + " when: " + msg.when
+                            + " seq: " + msgNode.mInsertSeq + "barrier: "
+                            + msgNode.isBarrier() + " now: " + SystemClock.uptimeMillis());
+                }
+                if (asyncMsgNode != null) {
+                    Message msg = asyncMsgNode.mMessage;
+                    Log.d(TAG_C, "Next found async node what: " + msg.what + " when: " + msg.when
+                            + " seq: " + asyncMsgNode.mInsertSeq + "barrier: "
+                            + asyncMsgNode.isBarrier() + " now: "
+                            + SystemClock.uptimeMillis());
+                }
+            }
+
+            /*
+             * the node which we will return, null if none are ready
+             */
+            MessageNode found = null;
+            /*
+             * The node from which we will determine our next wakeup time.
+             * Null indicates there is no next message ready. If we found a node,
+             * we can leave this null as Looper will call us again after delivering
+             * the message.
+             */
+            MessageNode next = null;
+
+            long now = SystemClock.uptimeMillis();
+            /*
+             * If we have a barrier we should return the async node (if it exists and is ready)
+             */
+            if (msgNode != null && msgNode.isBarrier()) {
+                if (asyncMsgNode != null && now >= asyncMsgNode.getWhen()) {
+                    found = asyncMsgNode;
+                } else {
+                    next = asyncMsgNode;
+                }
+            } else { /* No barrier. */
+                MessageNode earliest;
+                /*
+                 * If we have two messages, pick the earliest option from either queue.
+                 * Otherwise grab whichever node is non-null. If both are null we'll fall through.
+                 */
+                earliest = pickEarliestNode(msgNode, asyncMsgNode);
+
+                if (earliest != null) {
+                    if (now >= earliest.getWhen()) {
+                        found = earliest;
+                    } else {
+                        next = earliest;
+                    }
+                }
+            }
+
+            if (DEBUG) {
+                if (found != null) {
+                    Message msg = found.mMessage;
+                    Log.d(TAG_C, " Will deliver node what: " + msg.what + " when: " + msg.when
+                            + " seq: " + found.mInsertSeq + " barrier: " + found.isBarrier()
+                            + " async: " + found.isAsync() + " now: "
+                            + SystemClock.uptimeMillis());
+                } else {
+                    Log.d(TAG_C, "No node to deliver");
+                }
+                if (next != null) {
+                    Message msg = next.mMessage;
+                    Log.d(TAG_C, "Next node what: " + msg.what + " when: " + msg.when + " seq: "
+                            + next.mInsertSeq + " barrier: " + next.isBarrier() + " async: "
+                            + next.isAsync()
+                            + " now: " + SystemClock.uptimeMillis());
+                } else {
+                    Log.d(TAG_C, "No next node");
+                }
+            }
+
+            /*
+             * If we have a found message, we will get called again so there's no need to set state.
+             * In that case we can leave our state as ACTIVE.
+             *
+             * Otherwise we should determine how to park the thread.
+             */
+            StateNode nextOp = sStackStateActive;
+            if (found == null) {
+                if (next == null) {
+                    /* No message to deliver, sleep indefinitely */
+                    mNextPollTimeoutMillis = -1;
+                    nextOp = sStackStateParked;
+                    if (DEBUG) {
+                        Log.d(TAG_C, "nextMessage next state is StackStateParked");
+                    }
+                } else {
+                    /* Message not ready, or we found one to deliver already, set a timeout */
+                    long nextMessageWhen = next.getWhen();
+                    if (nextMessageWhen > now) {
+                        mNextPollTimeoutMillis = (int) Math.min(nextMessageWhen - now,
+                                Integer.MAX_VALUE);
+                    } else {
+                        mNextPollTimeoutMillis = 0;
+                    }
+
+                    mStackStateTimedPark.mWhenToWake = now + mNextPollTimeoutMillis;
+                    nextOp = mStackStateTimedPark;
+                    if (DEBUG) {
+                        Log.d(TAG_C, "nextMessage next state is StackStateTimedParked timeout ms "
+                                + mNextPollTimeoutMillis + " mWhenToWake: "
+                                + mStackStateTimedPark.mWhenToWake + " now " + now);
+                    }
+                }
+            }
+
+            /*
+             * Try to swap our state from Active back to Park or TimedPark. If we raced with
+             * enqueue, loop back around to pick up any new items.
+             */
+            if (sState.compareAndSet(this, sStackStateActive, nextOp)) {
+                mMessageCounts.clearCounts();
+                if (found != null) {
+                    if (!removeFromPriorityQueue(found)) {
+                        /*
+                         * RemoveMessages() might be able to pull messages out from under us
+                         * However we can detect that here and just loop around if it happens.
+                         */
+                        continue;
+                    }
+
+                    if (TRACE) {
+                        Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+                    }
+                    return found.mMessage;
+                }
+                return null;
+            }
+        }
+    }
+
+    private Message nextConcurrent() {
+        final long ptr = mPtr;
+        if (ptr == 0) {
+            return null;
+        }
+
+        mNextPollTimeoutMillis = 0;
+        int pendingIdleHandlerCount = -1; // -1 only during first iteration
+        while (true) {
+            if (mNextPollTimeoutMillis != 0) {
+                Binder.flushPendingCommands();
+            }
+
+            mMessageDirectlyQueued = false;
+            nativePollOnce(ptr, mNextPollTimeoutMillis);
+
+            Message msg = nextMessage();
+            if (msg != null) {
+                msg.markInUse();
+                return msg;
+            }
+
+            if ((boolean) sQuitting.getVolatile(this)) {
+                return null;
+            }
+
+            synchronized (mIdleHandlersLock) {
+                // If first time idle, then get the number of idlers to run.
+                // Idle handles only run if the queue is empty or if the first message
+                // in the queue (possibly a barrier) is due to be handled in the future.
+                if (pendingIdleHandlerCount < 0
+                        && isIdle()) {
+                    pendingIdleHandlerCount = mIdleHandlers.size();
+                }
+                if (pendingIdleHandlerCount <= 0) {
+                    // No idle handlers to run.  Loop and wait some more.
+                    continue;
+                }
+
+                if (mPendingIdleHandlers == null) {
+                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
+                }
+                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
+            }
+
+            // Run the idle handlers.
+            // We only ever reach this code block during the first iteration.
+            for (int i = 0; i < pendingIdleHandlerCount; i++) {
+                final IdleHandler idler = mPendingIdleHandlers[i];
+                mPendingIdleHandlers[i] = null; // release the reference to the handler
+
+                boolean keep = false;
+                try {
+                    keep = idler.queueIdle();
+                } catch (Throwable t) {
+                    Log.wtf(TAG_C, "IdleHandler threw exception", t);
+                }
+
+                if (!keep) {
+                    synchronized (mIdleHandlersLock) {
+                        mIdleHandlers.remove(idler);
+                    }
+                }
+            }
+
+            // Reset the idle handler count to 0 so we do not run them again.
+            pendingIdleHandlerCount = 0;
+
+            // While calling an idle handler, a new message could have been delivered
+            // so go back and look again for a pending message without waiting.
+            mNextPollTimeoutMillis = 0;
+        }
+    }
+
+    @UnsupportedAppUsage
+    Message next() {
+        if (sUseConcurrent) {
+            return nextConcurrent();
+        }
+
+        // Return here if the message loop has already quit and been disposed.
+        // This can happen if the application tries to restart a looper after quit
+        // which is not supported.
+        final long ptr = mPtr;
+        if (ptr == 0) {
+            return null;
+        }
+
+        int pendingIdleHandlerCount = -1; // -1 only during first iteration
+        int nextPollTimeoutMillis = 0;
+        for (;;) {
+            if (nextPollTimeoutMillis != 0) {
+                Binder.flushPendingCommands();
+            }
+
+            nativePollOnce(ptr, nextPollTimeoutMillis);
+
+            synchronized (this) {
+                // Try to retrieve the next message.  Return if found.
+                final long now = SystemClock.uptimeMillis();
+                Message prevMsg = null;
+                Message msg = mMessages;
+                if (msg != null && msg.target == null) {
+                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
+                    do {
+                        prevMsg = msg;
+                        msg = msg.next;
+                    } while (msg != null && !msg.isAsynchronous());
+                }
+                if (msg != null) {
+                    if (now < msg.when) {
+                        // Next message is not ready.  Set a timeout to wake up when it is ready.
+                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
+                    } else {
+                        // Got a message.
+                        mBlocked = false;
+                        if (prevMsg != null) {
+                            prevMsg.next = msg.next;
+                            if (prevMsg.next == null) {
+                                mLast = prevMsg;
+                            }
+                        } else {
+                            mMessages = msg.next;
+                            if (msg.next == null) {
+                                mLast = null;
+                            }
+                        }
+                        msg.next = null;
+                        if (DEBUG) Log.v(TAG_L, "Returning message: " + msg);
+                        msg.markInUse();
+                        if (msg.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
+                        if (TRACE) {
+                            Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
+                        }
+                        return msg;
+                    }
+                } else {
+                    // No more messages.
+                    nextPollTimeoutMillis = -1;
+                }
+
+                // Process the quit message now that all pending messages have been handled.
+                if (mQuitting) {
+                    dispose();
+                    return null;
+                }
+
+                // If first time idle, then get the number of idlers to run.
+                // Idle handles only run if the queue is empty or if the first message
+                // in the queue (possibly a barrier) is due to be handled in the future.
+                if (pendingIdleHandlerCount < 0
+                        && (mMessages == null || now < mMessages.when)) {
+                    pendingIdleHandlerCount = mIdleHandlers.size();
+                }
+                if (pendingIdleHandlerCount <= 0) {
+                    // No idle handlers to run.  Loop and wait some more.
+                    mBlocked = true;
+                    continue;
+                }
+
+                if (mPendingIdleHandlers == null) {
+                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
+                }
+                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
+            }
+
+            // Run the idle handlers.
+            // We only ever reach this code block during the first iteration.
+            for (int i = 0; i < pendingIdleHandlerCount; i++) {
+                final IdleHandler idler = mPendingIdleHandlers[i];
+                mPendingIdleHandlers[i] = null; // release the reference to the handler
+
+                boolean keep = false;
+                try {
+                    keep = idler.queueIdle();
+                } catch (Throwable t) {
+                    Log.wtf(TAG_L, "IdleHandler threw exception", t);
+                }
+
+                if (!keep) {
+                    synchronized (this) {
+                        mIdleHandlers.remove(idler);
+                    }
+                }
+            }
+
+            // Reset the idle handler count to 0 so we do not run them again.
+            pendingIdleHandlerCount = 0;
+
+            // While calling an idle handler, a new message could have been delivered
+            // so go back and look again for a pending message without waiting.
+            nextPollTimeoutMillis = 0;
+        }
+    }
+
+    void quit(boolean safe) {
+        if (!mQuitAllowed) {
+            throw new IllegalStateException("Main thread not allowed to quit.");
+        }
+
+        if (sUseConcurrent) {
+            synchronized (mIdleHandlersLock) {
+                if (sQuitting.compareAndSet(this, false, true)) {
+                    if (safe) {
+                        removeAllFutureMessages();
+                    } else {
+                        removeAllMessages();
+                    }
+
+                    // We can assume mPtr != 0 because sQuitting was previously false.
+                    nativeWake(mPtr);
+                }
+            }
+        } else {
+            synchronized (this) {
+                if (mQuitting) {
+                    return;
+                }
+                mQuitting = true;
+
+                if (safe) {
+                    removeAllFutureMessagesLocked();
+                } else {
+                    removeAllMessagesLocked();
+                }
+
+                // We can assume mPtr != 0 because mQuitting was previously false.
+                nativeWake(mPtr);
+            }
+        }
+    }
+
+    /**
+     * Posts a synchronization barrier to the Looper's message queue.
+     *
+     * Message processing occurs as usual until the message queue encounters the
+     * synchronization barrier that has been posted.  When the barrier is encountered,
+     * later synchronous messages in the queue are stalled (prevented from being executed)
+     * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
+     * the token that identifies the synchronization barrier.
+     *
+     * This method is used to immediately postpone execution of all subsequently posted
+     * synchronous messages until a condition is met that releases the barrier.
+     * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
+     * and continue to be processed as usual.
+     *
+     * This call must be always matched by a call to {@link #removeSyncBarrier} with
+     * the same token to ensure that the message queue resumes normal operation.
+     * Otherwise the application will probably hang!
+     *
+     * @return A token that uniquely identifies the barrier.  This token must be
+     * passed to {@link #removeSyncBarrier} to release the barrier.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public int postSyncBarrier() {
+        return postSyncBarrier(SystemClock.uptimeMillis());
+    }
+
+    private int postSyncBarrier(long when) {
+        // Enqueue a new sync barrier token.
+        // We don't need to wake the queue because the purpose of a barrier is to stall it.
+        if (sUseConcurrent) {
+            final int token = mNextBarrierTokenAtomic.getAndIncrement();
+
+            // b/376573804: apps and tests may expect to be able to use reflection
+            // to read this value. Make some effort to support this legacy use case.
+            mNextBarrierToken = token + 1;
+
+            final Message msg = Message.obtain();
+
+            msg.markInUse();
+            msg.arg1 = token;
+
+            if (!enqueueMessageUnchecked(msg, when)) {
+                Log.wtf(TAG_C, "Unexpected error while adding sync barrier!");
+                return -1;
+            }
+
+            return token;
+        }
+
+        synchronized (this) {
+            final int token = mNextBarrierToken++;
+            final Message msg = Message.obtain();
+            msg.markInUse();
+            msg.when = when;
+            msg.arg1 = token;
+
+            if (Flags.messageQueueTailTracking() && mLast != null && mLast.when <= when) {
+                /* Message goes to tail of list */
+                mLast.next = msg;
+                mLast = msg;
+                msg.next = null;
+                return token;
+            }
+
+            Message prev = null;
+            Message p = mMessages;
+            if (when != 0) {
+                while (p != null && p.when <= when) {
+                    prev = p;
+                    p = p.next;
+                }
+            }
+
+            if (p == null) {
+                /* We reached the tail of the list, or list is empty. */
+                mLast = msg;
+            }
+
+            if (prev != null) { // invariant: p == prev.next
+                msg.next = p;
+                prev.next = msg;
+            } else {
+                msg.next = p;
+                mMessages = msg;
+            }
+            return token;
+        }
+    }
+
+    private static final class MatchBarrierToken extends MessageCompare {
+        int mBarrierToken;
+
+        MatchBarrierToken(int token) {
+            super();
+            mBarrierToken = token;
+        }
+
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == null && m.arg1 == mBarrierToken) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Removes a synchronization barrier.
+     *
+     * @param token The synchronization barrier token that was returned by
+     * {@link #postSyncBarrier}.
+     *
+     * @throws IllegalStateException if the barrier was not found.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public void removeSyncBarrier(int token) {
+        // Remove a sync barrier token from the queue.
+        // If the queue is no longer stalled by a barrier then wake it.
+        if (sUseConcurrent) {
+            boolean removed;
+            MessageNode first;
+            final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token);
+
+            try {
+                /* Retain the first element to see if we are currently stuck on a barrier. */
+                first = mPriorityQueue.first();
+            } catch (NoSuchElementException e) {
+                /* The queue is empty */
+                first = null;
+            }
+
+            removed = findOrRemoveMessages(null, 0, null, null, 0, matchBarrierToken, true);
+            if (removed && first != null) {
+                Message m = first.mMessage;
+                if (m.target == null && m.arg1 == token) {
+                    /* Wake up next() in case it was sleeping on this barrier. */
+                    nativeWake(mPtr);
+                }
+            } else if (!removed) {
+                throw new IllegalStateException("The specified message queue synchronization "
+                        + " barrier token has not been posted or has already been removed.");
+            }
+            return;
+        }
+
+        synchronized (this) {
+            Message prev = null;
+            Message p = mMessages;
+            while (p != null && (p.target != null || p.arg1 != token)) {
+                prev = p;
+                p = p.next;
+            }
+            if (p == null) {
+                throw new IllegalStateException("The specified message queue synchronization "
+                        + " barrier token has not been posted or has already been removed.");
+            }
+            final boolean needWake;
+            if (prev != null) {
+                prev.next = p.next;
+                if (prev.next == null) {
+                    mLast = prev;
+                }
+                needWake = false;
+            } else {
+                mMessages = p.next;
+                if (mMessages == null) {
+                    mLast = null;
+                }
+                needWake = mMessages == null || mMessages.target != null;
+            }
+            p.recycleUnchecked();
+
+            // If the loop is quitting then it is already awake.
+            // We can assume mPtr != 0 when mQuitting is false.
+            if (needWake && !mQuitting) {
+                nativeWake(mPtr);
+            }
+        }
+    }
+
+    boolean enqueueMessage(Message msg, long when) {
+        if (msg.target == null) {
+            throw new IllegalArgumentException("Message must have a target.");
+        }
+
+        if (sUseConcurrent) {
+            if (msg.isInUse()) {
+                throw new IllegalStateException(msg + " This message is already in use.");
+            }
+
+            return enqueueMessageUnchecked(msg, when);
+        }
+
+        synchronized (this) {
+            if (msg.isInUse()) {
+                throw new IllegalStateException(msg + " This message is already in use.");
+            }
+
+            if (mQuitting) {
+                IllegalStateException e = new IllegalStateException(
+                        msg.target + " sending message to a Handler on a dead thread");
+                Log.w(TAG_L, e.getMessage(), e);
+                msg.recycle();
+                return false;
+            }
+
+            msg.markInUse();
+            msg.when = when;
+            Message p = mMessages;
+            boolean needWake;
+            if (p == null || when == 0 || when < p.when) {
+                // New head, wake up the event queue if blocked.
+                msg.next = p;
+                mMessages = msg;
+                needWake = mBlocked;
+                if (p == null) {
+                    mLast = mMessages;
+                }
+            } else {
+                // Message is to be inserted at tail or middle of queue. Usually we don't have to
+                // wake up the event queue unless there is a barrier at the head of the queue and
+                // the message is the earliest asynchronous message in the queue.
+                needWake = mBlocked && p.target == null && msg.isAsynchronous();
+
+                // For readability, we split this portion of the function into two blocks based on
+                // whether tail tracking is enabled. This has a minor implication for the case
+                // where tail tracking is disabled. See the comment below.
+                if (Flags.messageQueueTailTracking()) {
+                    if (when >= mLast.when) {
+                        needWake = needWake && mAsyncMessageCount == 0;
+                        msg.next = null;
+                        mLast.next = msg;
+                        mLast = msg;
+                    } else {
+                        // Inserted within the middle of the queue.
+                        Message prev;
+                        for (;;) {
+                            prev = p;
+                            p = p.next;
+                            if (p == null || when < p.when) {
+                                break;
+                            }
+                            if (needWake && p.isAsynchronous()) {
+                                needWake = false;
+                            }
+                        }
+                        if (p == null) {
+                            /* Inserting at tail of queue */
+                            mLast = msg;
+                        }
+                        msg.next = p; // invariant: p == prev.next
+                        prev.next = msg;
+                    }
+                } else {
+                    Message prev;
+                    for (;;) {
+                        prev = p;
+                        p = p.next;
+                        if (p == null || when < p.when) {
+                            break;
+                        }
+                        if (needWake && p.isAsynchronous()) {
+                            needWake = false;
+                        }
+                    }
+                    msg.next = p; // invariant: p == prev.next
+                    prev.next = msg;
+
+                    /*
+                     * If this block is executing then we have a build without tail tracking -
+                     * specifically: Flags.messageQueueTailTracking() == false. This is determined
+                     * at build time so the flag won't change on us during runtime.
+                     *
+                     * Since we don't want to pepper the code with extra checks, we only check
+                     * for tail tracking when we might use mLast. Otherwise, we continue to update
+                     * mLast as the tail of the list.
+                     *
+                     * In this case however we are not maintaining mLast correctly. Since we never
+                     * use it, this is fine. However, we run the risk of leaking a reference.
+                     * So set mLast to null in this case to avoid any Message leaks. The other
+                     * sites will never use the value so we are safe against null pointer derefs.
+                     */
+                    mLast = null;
+                }
+            }
+
+            if (msg.isAsynchronous()) {
+                mAsyncMessageCount++;
+            }
+
+            // We can assume mPtr != 0 because mQuitting is false.
+            if (needWake) {
+                nativeWake(mPtr);
+            }
+        }
+        return true;
+    }
+
+    private static final class MatchHandlerWhatAndObject extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && m.what == what && (object == null || m.obj == object)) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchHandlerWhatAndObject mMatchHandlerWhatAndObject =
+            new MatchHandlerWhatAndObject();
+    boolean hasMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+        if (sUseConcurrent) {
+            return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject,
+                    false);
+        }
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
+    private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchHandlerWhatAndObjectEquals mMatchHandlerWhatAndObjectEquals =
+            new MatchHandlerWhatAndObjectEquals();
+    boolean hasEqualMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+        if (sUseConcurrent) {
+            return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals,
+                    false);
+
+        }
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h && p.what == what && (object == null || object.equals(p.obj))) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
+    private static final class MatchHandlerRunnableAndObject extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && m.callback == r && (object == null || m.obj == object)) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchHandlerRunnableAndObject mMatchHandlerRunnableAndObject =
+            new MatchHandlerRunnableAndObject();
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    boolean hasMessages(Handler h, Runnable r, Object object) {
+        if (h == null) {
+            return false;
+        }
+        if (sUseConcurrent) {
+            return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject,
+                    false);
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
+    private static final class MatchHandler extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchHandler mMatchHandler = new MatchHandler();
+    boolean hasMessages(Handler h) {
+        if (h == null) {
+            return false;
+        }
+        if (sUseConcurrent) {
+            return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false);
+        }
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
+    void removeMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return;
+        }
+        if (sUseConcurrent) {
+            findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true);
+            return;
+        }
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.what == what
+                   && (object == null || p.obj == object)) {
+                Message n = p.next;
+                mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            if (p == null) {
+                mLast = mMessages;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.what == what
+                            && (object == null || n.obj == object)) {
+                        Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    void removeEqualMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        if (sUseConcurrent) {
+            findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true);
+            return;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.what == what
+                   && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            if (p == null) {
+                mLast = mMessages;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.what == what
+                            && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    void removeMessages(Handler h, Runnable r, Object object) {
+        if (h == null || r == null) {
+            return;
+        }
+
+        if (sUseConcurrent) {
+            findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
+            return;
+        }
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.callback == r
+                   && (object == null || p.obj == object)) {
+                Message n = p.next;
+                mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            if (p == null) {
+                mLast = mMessages;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.callback == r
+                            && (object == null || n.obj == object)) {
+                        Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchHandlerRunnableAndObjectEquals mMatchHandlerRunnableAndObjectEquals =
+            new MatchHandlerRunnableAndObjectEquals();
+    void removeEqualMessages(Handler h, Runnable r, Object object) {
+        if (h == null || r == null) {
+            return;
+        }
+
+        if (sUseConcurrent) {
+            findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
+            return;
+        }
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.callback == r
+                   && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            if (p == null) {
+                mLast = mMessages;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.callback == r
+                            && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    private static final class MatchHandlerAndObject extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && (object == null || m.obj == object)) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchHandlerAndObject mMatchHandlerAndObject = new MatchHandlerAndObject();
+    void removeCallbacksAndMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        if (sUseConcurrent) {
+            findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true);
+            return;
+        }
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h
+                    && (object == null || p.obj == object)) {
+                Message n = p.next;
+                mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            if (p == null) {
+                mLast = mMessages;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && (object == null || n.obj == object)) {
+                        Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    private static final class MatchHandlerAndObjectEquals extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.target == h && (object == null || object.equals(m.obj))) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchHandlerAndObjectEquals mMatchHandlerAndObjectEquals =
+            new MatchHandlerAndObjectEquals();
+    void removeCallbacksAndEqualMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        if (sUseConcurrent) {
+            findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
+            return;
+        }
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h
+                    && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            if (p == null) {
+                mLast = mMessages;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
+    private void removeAllMessagesLocked() {
+        Message p = mMessages;
+        while (p != null) {
+            Message n = p.next;
+            p.recycleUnchecked();
+            p = n;
+        }
+        mMessages = null;
+        mLast = null;
+        mAsyncMessageCount = 0;
+    }
+
+    private void removeAllFutureMessagesLocked() {
+        final long now = SystemClock.uptimeMillis();
+        Message p = mMessages;
+        if (p != null) {
+            if (p.when > now) {
+                removeAllMessagesLocked();
+            } else {
+                Message n;
+                for (;;) {
+                    n = p.next;
+                    if (n == null) {
+                        return;
+                    }
+                    if (n.when > now) {
+                        break;
+                    }
+                    p = n;
+                }
+                p.next = null;
+                mLast = p;
+
+                do {
+                    p = n;
+                    n = p.next;
+                    if (p.isAsynchronous()) {
+                        mAsyncMessageCount--;
+                    }
+                    p.recycleUnchecked();
+                } while (n != null);
+            }
+        }
+    }
+
+    private static final class MatchAllMessages extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            return true;
+        }
+    }
+    private final MatchAllMessages mMatchAllMessages = new MatchAllMessages();
+    private void removeAllMessages() {
+        findOrRemoveMessages(null, -1, null, null, 0, mMatchAllMessages, true);
+    }
+
+    private static final class MatchAllFutureMessages extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.when > when) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchAllFutureMessages mMatchAllFutureMessages = new MatchAllFutureMessages();
+    private void removeAllFutureMessages() {
+        findOrRemoveMessages(null, -1, null, null, SystemClock.uptimeMillis(),
+                mMatchAllFutureMessages, true);
+    }
+
+    @NeverCompile
+    private void printPriorityQueueNodes() {
+        Iterator<MessageNode> iterator = mPriorityQueue.iterator();
+
+        Log.d(TAG_C, "* Dump priority queue");
+        while (iterator.hasNext()) {
+            MessageNode msgNode = iterator.next();
+            Log.d(TAG_C, "** MessageNode what: " + msgNode.mMessage.what + " when "
+                    + msgNode.mMessage.when + " seq: " + msgNode.mInsertSeq);
+        }
+    }
+
+    @NeverCompile
+    private int dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue, Printer pw,
+            String prefix, Handler h, int n) {
+        int count = 0;
+        long now = SystemClock.uptimeMillis();
+
+        for (MessageNode msgNode : queue) {
+            Message msg = msgNode.mMessage;
+            if (h == null || h == msg.target) {
+                pw.println(prefix + "Message " + (n + count) + ": " + msg.toString(now));
+            }
+            count++;
+        }
+        return count;
+    }
+
+    @NeverCompile
+    void dump(Printer pw, String prefix, Handler h) {
+        if (sUseConcurrent) {
+            long now = SystemClock.uptimeMillis();
+            int n = 0;
+
+            pw.println(prefix + "(MessageQueue is using Concurrent implementation)");
+
+            StackNode node = (StackNode) sState.getVolatile(this);
+            while (node != null) {
+                if (node.isMessageNode()) {
+                    Message msg = ((MessageNode) node).mMessage;
+                    if (h == null || h == msg.target) {
+                        pw.println(prefix + "Message " + n + ": " + msg.toString(now));
+                    }
+                    node = ((MessageNode) node).mNext;
+                } else {
+                    pw.println(prefix + "State: " + node);
+                    node = null;
+                }
+                n++;
+            }
+
+            pw.println(prefix + "PriorityQueue Messages: ");
+            n += dumpPriorityQueue(mPriorityQueue, pw, prefix, h, n);
+            pw.println(prefix + "AsyncPriorityQueue Messages: ");
+            n += dumpPriorityQueue(mAsyncPriorityQueue, pw, prefix, h, n);
+
+            pw.println(prefix + "(Total messages: " + n + ", polling=" + isPolling()
+                    + ", quitting=" + (boolean) sQuitting.getVolatile(this) + ")");
+            return;
+        }
+
+        synchronized (this) {
+            pw.println(prefix + "(MessageQueue is using Legacy implementation)");
+            long now = SystemClock.uptimeMillis();
+            int n = 0;
+            for (Message msg = mMessages; msg != null; msg = msg.next) {
+                if (h == null || h == msg.target) {
+                    pw.println(prefix + "Message " + n + ": " + msg.toString(now));
+                }
+                n++;
+            }
+            pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
+                    + ", quitting=" + mQuitting + ")");
+        }
+    }
+
+    @NeverCompile
+    private int dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue,
+            ProtoOutputStream proto) {
+        int count = 0;
+
+        for (MessageNode msgNode : queue) {
+            Message msg = msgNode.mMessage;
+            msg.dumpDebug(proto, MessageQueueProto.MESSAGES);
+            count++;
+        }
+        return count;
+    }
+
+    @NeverCompile
+    void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        if (sUseConcurrent) {
+            final long messageQueueToken = proto.start(fieldId);
+
+            StackNode node = (StackNode) sState.getVolatile(this);
+            while (node.isMessageNode()) {
+                Message msg = ((MessageNode) node).mMessage;
+                msg.dumpDebug(proto, MessageQueueProto.MESSAGES);
+                node = ((MessageNode) node).mNext;
+            }
+
+            dumpPriorityQueue(mPriorityQueue, proto);
+            dumpPriorityQueue(mAsyncPriorityQueue, proto);
+
+            proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPolling());
+            proto.write(MessageQueueProto.IS_QUITTING, (boolean) sQuitting.getVolatile(this));
+            proto.end(messageQueueToken);
+            return;
+        }
+
+        final long messageQueueToken = proto.start(fieldId);
+        synchronized (this) {
+            for (Message msg = mMessages; msg != null; msg = msg.next) {
+                msg.dumpDebug(proto, MessageQueueProto.MESSAGES);
+            }
+            proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked());
+            proto.write(MessageQueueProto.IS_QUITTING, mQuitting);
+        }
+        proto.end(messageQueueToken);
+    }
+
+    /**
+     * Callback interface for discovering when a thread is going to block
+     * waiting for more messages.
+     */
+    public static interface IdleHandler {
+        /**
+         * Called when the message queue has run out of messages and will now
+         * wait for more.  Return true to keep your idle handler active, false
+         * to have it removed.  This may be called if there are still messages
+         * pending in the queue, but they are all scheduled to be dispatched
+         * after the current time.
+         */
+        boolean queueIdle();
+    }
+
+    /**
+     * A listener which is invoked when file descriptor related events occur.
+     */
+    public interface OnFileDescriptorEventListener {
+        /**
+         * File descriptor event: Indicates that the file descriptor is ready for input
+         * operations, such as reading.
+         * <p>
+         * The listener should read all available data from the file descriptor
+         * then return <code>true</code> to keep the listener active or <code>false</code>
+         * to remove the listener.
+         * </p><p>
+         * In the case of a socket, this event may be generated to indicate
+         * that there is at least one incoming connection that the listener
+         * should accept.
+         * </p><p>
+         * This event will only be generated if the {@link #EVENT_INPUT} event mask was
+         * specified when the listener was added.
+         * </p>
+         */
+        public static final int EVENT_INPUT = 1 << 0;
+
+        /**
+         * File descriptor event: Indicates that the file descriptor is ready for output
+         * operations, such as writing.
+         * <p>
+         * The listener should write as much data as it needs.  If it could not
+         * write everything at once, then it should return <code>true</code> to
+         * keep the listener active.  Otherwise, it should return <code>false</code>
+         * to remove the listener then re-register it later when it needs to write
+         * something else.
+         * </p><p>
+         * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was
+         * specified when the listener was added.
+         * </p>
+         */
+        public static final int EVENT_OUTPUT = 1 << 1;
+
+        /**
+         * File descriptor event: Indicates that the file descriptor encountered a
+         * fatal error.
+         * <p>
+         * File descriptor errors can occur for various reasons.  One common error
+         * is when the remote peer of a socket or pipe closes its end of the connection.
+         * </p><p>
+         * This event may be generated at any time regardless of whether the
+         * {@link #EVENT_ERROR} event mask was specified when the listener was added.
+         * </p>
+         */
+        public static final int EVENT_ERROR = 1 << 2;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = { "EVENT_" }, value = {
+                EVENT_INPUT,
+                EVENT_OUTPUT,
+                EVENT_ERROR
+        })
+        public @interface Events {}
+
+        /**
+         * Called when a file descriptor receives events.
+         *
+         * @param fd The file descriptor.
+         * @param events The set of events that occurred: a combination of the
+         * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks.
+         * @return The new set of events to watch, or 0 to unregister the listener.
+         *
+         * @see #EVENT_INPUT
+         * @see #EVENT_OUTPUT
+         * @see #EVENT_ERROR
+         */
+        @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events);
+    }
+
+    private static final class FileDescriptorRecord {
+        public final FileDescriptor mDescriptor;
+        public int mEvents;
+        public OnFileDescriptorEventListener mListener;
+        public int mSeq;
+
+        public FileDescriptorRecord(FileDescriptor descriptor,
+                int events, OnFileDescriptorEventListener listener) {
+            mDescriptor = descriptor;
+            mEvents = events;
+            mListener = listener;
+        }
+    }
+
+    /**
+     * ConcurrentMessageQueue specific classes methods and variables
+     */
+    /* Helper to choose the correct queue to insert into. */
+    private void insertIntoPriorityQueue(MessageNode msgNode) {
+        if (msgNode.isAsync()) {
+            mAsyncPriorityQueue.add(msgNode);
+        } else {
+            mPriorityQueue.add(msgNode);
+        }
+    }
+
+    private boolean removeFromPriorityQueue(MessageNode msgNode) {
+        if (msgNode.isAsync()) {
+            return mAsyncPriorityQueue.remove(msgNode);
+        } else {
+            return mPriorityQueue.remove(msgNode);
+        }
+    }
+
+    private MessageNode pickEarliestNode(MessageNode nodeA, MessageNode nodeB) {
+        if (nodeA != null && nodeB != null) {
+            if (nodeA.compareTo(nodeB) < 0) {
+                return nodeA;
+            }
+            return nodeB;
+        }
+
+        return nodeA != null ? nodeA : nodeB;
+    }
+
+    private MessageNode iterateNext(Iterator<MessageNode> iter) {
+        if (iter.hasNext()) {
+            try {
+                return iter.next();
+            } catch (NoSuchElementException e) {
+                /* The queue is empty - this can happen if we race with remove */
+            }
+        }
+        return null;
+    }
+
+    /* Move any non-cancelled messages into the priority queue */
+    private void drainStack(StackNode oldTop) {
+        while (oldTop.isMessageNode()) {
+            MessageNode oldTopMessageNode = (MessageNode) oldTop;
+            if (oldTopMessageNode.removeFromStack()) {
+                insertIntoPriorityQueue(oldTopMessageNode);
+            }
+            MessageNode inserted = oldTopMessageNode;
+            oldTop = oldTopMessageNode.mNext;
+            /*
+             * removeMessages can walk this list while we are consuming it.
+             * Set our next pointer to null *after* we add the message to our
+             * priority queue. This way removeMessages() will always find the
+             * message, either in our list or in the priority queue.
+             */
+            inserted.mNext = null;
+        }
+    }
+
+    /* Set the stack state to Active, return a list of nodes to walk. */
+    private StackNode swapAndSetStackStateActive() {
+        while (true) {
+            /* Set stack state to Active, get node list to walk later */
+            StackNode current = (StackNode) sState.getVolatile(this);
+            if (current == sStackStateActive
+                    || sState.compareAndSet(this, current, sStackStateActive)) {
+                return current;
+            }
+        }
+    }
+    private StateNode getStateNode(StackNode node) {
+        if (node.isMessageNode()) {
+            return ((MessageNode) node).mBottomOfStack;
+        }
+        return (StateNode) node;
+    }
+
+    private void waitForDrainCompleted() {
+        mDrainingLock.lock();
+        while (mNextIsDrainingStack) {
+            mDrainCompleted.awaitUninterruptibly();
+        }
+        mDrainingLock.unlock();
+    }
+
+    @IntDef(value = {
+        STACK_NODE_MESSAGE,
+        STACK_NODE_ACTIVE,
+        STACK_NODE_PARKED,
+        STACK_NODE_TIMEDPARK})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface StackNodeType {}
+
+    /*
+     * Stack node types. STACK_NODE_MESSAGE indicates a node containing a message.
+     * The other types indicate what state our Looper thread is in. The bottom of
+     * the stack is always a single state node. Message nodes are added on top.
+     */
+    private static final int STACK_NODE_MESSAGE = 0;
+    /*
+     * Active state indicates that next() is processing messages
+     */
+    private static final int STACK_NODE_ACTIVE = 1;
+    /*
+     * Parked state indicates that the Looper thread is sleeping indefinitely (nothing to deliver)
+     */
+    private static final int STACK_NODE_PARKED = 2;
+    /*
+     * Timed Park state indicates that the Looper thread is sleeping, waiting for a message
+     * deadline
+     */
+    private static final int STACK_NODE_TIMEDPARK = 3;
+
+    /* Describes a node in the Treiber stack */
+    static class StackNode {
+        @StackNodeType
+        private final int mType;
+
+        StackNode(@StackNodeType int type) {
+            mType = type;
+        }
+
+        @StackNodeType
+        final int getNodeType() {
+            return mType;
+        }
+
+        final boolean isMessageNode() {
+            return mType == STACK_NODE_MESSAGE;
+        }
+    }
+
+    static final class MessageNode extends StackNode implements Comparable<MessageNode> {
+        private final Message mMessage;
+        volatile StackNode mNext;
+        StateNode mBottomOfStack;
+        boolean mWokeUp;
+        final long mInsertSeq;
+        private static final VarHandle sRemovedFromStack;
+        private volatile boolean mRemovedFromStackValue;
+        static {
+            try {
+                MethodHandles.Lookup l = MethodHandles.lookup();
+                sRemovedFromStack = l.findVarHandle(MessageQueue.MessageNode.class,
+                        "mRemovedFromStackValue", boolean.class);
+            } catch (Exception e) {
+                Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e);
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+        MessageNode(@NonNull Message message, long insertSeq) {
+            super(STACK_NODE_MESSAGE);
+            mMessage = message;
+            mInsertSeq = insertSeq;
+        }
+
+        long getWhen() {
+            return mMessage.when;
+        }
+
+        boolean isRemovedFromStack() {
+            return mRemovedFromStackValue;
+        }
+
+        boolean removeFromStack() {
+            return sRemovedFromStack.compareAndSet(this, false, true);
+        }
+
+        boolean isAsync() {
+            return mMessage.isAsynchronous();
+        }
+
+        boolean isBarrier() {
+            return mMessage.target == null;
+        }
+
+        @Override
+        public int compareTo(@NonNull MessageNode messageNode) {
+            Message other = messageNode.mMessage;
+
+            int compared = Long.compare(mMessage.when, other.when);
+            if (compared == 0) {
+                compared = Long.compare(mInsertSeq, messageNode.mInsertSeq);
+            }
+            return compared;
+        }
+    }
+
+    static class StateNode extends StackNode {
+        StateNode(int type) {
+            super(type);
+        }
+    }
+
+    static final class TimedParkStateNode extends StateNode {
+        long mWhenToWake;
+
+        TimedParkStateNode() {
+            super(STACK_NODE_TIMEDPARK);
+        }
+    }
+
+    private static final StateNode sStackStateActive = new StateNode(STACK_NODE_ACTIVE);
+    private static final StateNode sStackStateParked = new StateNode(STACK_NODE_PARKED);
+    private final TimedParkStateNode mStackStateTimedPark = new TimedParkStateNode();
+
+    /* This is the top of our treiber stack. */
+    private static final VarHandle sState;
+    static {
+        try {
+            MethodHandles.Lookup l = MethodHandles.lookup();
+            sState = l.findVarHandle(MessageQueue.class, "mStateValue",
+                    MessageQueue.StackNode.class);
+        } catch (Exception e) {
+            Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e);
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private volatile StackNode mStateValue = sStackStateParked;
+    private final ConcurrentSkipListSet<MessageNode> mPriorityQueue =
+            new ConcurrentSkipListSet<MessageNode>();
+    private final ConcurrentSkipListSet<MessageNode> mAsyncPriorityQueue =
+            new ConcurrentSkipListSet<MessageNode>();
+
+    /*
+     * This helps us ensure that messages with the same timestamp are inserted in FIFO order.
+     * Increments on each insert, starting at 0. MessageNode.compareTo() will compare sequences
+     * when delivery timestamps are identical.
+     */
+    private static final VarHandle sNextInsertSeq;
+    private volatile long mNextInsertSeqValue = 0;
+    /*
+     * The exception to the FIFO order rule is sendMessageAtFrontOfQueue().
+     * Those messages must be in LIFO order.
+     * Decrements on each front of queue insert.
+     */
+    private static final VarHandle sNextFrontInsertSeq;
+    private volatile long mNextFrontInsertSeqValue = -1;
+    static {
+        try {
+            MethodHandles.Lookup l = MethodHandles.lookup();
+            sNextInsertSeq = l.findVarHandle(MessageQueue.class, "mNextInsertSeqValue",
+                    long.class);
+            sNextFrontInsertSeq = l.findVarHandle(MessageQueue.class, "mNextFrontInsertSeqValue",
+                    long.class);
+        } catch (Exception e) {
+            Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e);
+            throw new ExceptionInInitializerError(e);
+        }
+
+    }
+
+    /*
+     * Tracks the number of queued and cancelled messages in our stack.
+     *
+     * On item cancellation, determine whether to wake next() to flush tombstoned messages.
+     * We track queued and cancelled counts as two ints packed into a single long.
+     */
+    private static final class MessageCounts {
+        private static VarHandle sCounts;
+        private volatile long mCountsValue = 0;
+        static {
+            try {
+                MethodHandles.Lookup l = MethodHandles.lookup();
+                sCounts = l.findVarHandle(MessageQueue.MessageCounts.class, "mCountsValue",
+                        long.class);
+            } catch (Exception e) {
+                Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e);
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+        /* We use a special value to indicate when next() has been woken for flush. */
+        private static final long AWAKE = Long.MAX_VALUE;
+        /*
+         * Minimum number of messages in the stack which we need before we consider flushing
+         * tombstoned items.
+         */
+        private static final int MESSAGE_FLUSH_THRESHOLD = 10;
+
+        private static int numQueued(long val) {
+            return (int) (val >>> Integer.SIZE);
+        }
+
+        private static int numCancelled(long val) {
+            return (int) val;
+        }
+
+        private static long combineCounts(int queued, int cancelled) {
+            return ((long) queued << Integer.SIZE) | (long) cancelled;
+        }
+
+        public void incrementQueued() {
+            while (true) {
+                long oldVal = mCountsValue;
+                int queued = numQueued(oldVal);
+                int cancelled = numCancelled(oldVal);
+                /* Use Math.max() to avoid overflow of queued count */
+                long newVal = combineCounts(Math.max(queued + 1, queued), cancelled);
+
+                /* Don't overwrite 'AWAKE' state */
+                if (oldVal == AWAKE || sCounts.compareAndSet(this, oldVal, newVal)) {
+                    break;
+                }
+            }
+        }
+
+        public boolean incrementCancelled() {
+            while (true) {
+                long oldVal = mCountsValue;
+                if (oldVal == AWAKE) {
+                    return false;
+                }
+                int queued = numQueued(oldVal);
+                int cancelled = numCancelled(oldVal);
+                boolean needsPurge = queued > MESSAGE_FLUSH_THRESHOLD
+                        && (queued >> 1) < cancelled;
+                long newVal;
+                if (needsPurge) {
+                    newVal = AWAKE;
+                } else {
+                    newVal = combineCounts(queued,
+                            Math.max(cancelled + 1, cancelled));
+                }
+
+                if (sCounts.compareAndSet(this, oldVal, newVal)) {
+                    return needsPurge;
+                }
+            }
+        }
+
+        public void clearCounts() {
+            mCountsValue = 0;
+        }
+    }
+
+    private final MessageCounts mMessageCounts = new MessageCounts();
+
+    private final Object mIdleHandlersLock = new Object();
+    private final Object mFileDescriptorRecordsLock = new Object();
+
+    private static final VarHandle sQuitting;
+    private boolean mQuittingValue = false;
+    static {
+        try {
+            MethodHandles.Lookup l = MethodHandles.lookup();
+            sQuitting = l.findVarHandle(MessageQueue.class, "mQuittingValue", boolean.class);
+        } catch (Exception e) {
+            Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e);
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    // The next barrier token.
+    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
+    private final AtomicInteger mNextBarrierTokenAtomic = new AtomicInteger(1);
+
+    // Must retain this for compatibility reasons.
+    @UnsupportedAppUsage
+    private int mNextBarrierToken;
+
+    /* Protects mNextIsDrainingStack */
+    private final ReentrantLock mDrainingLock = new ReentrantLock();
+    private boolean mNextIsDrainingStack = false;
+    private final Condition mDrainCompleted = mDrainingLock.newCondition();
+
+    private boolean enqueueMessageUnchecked(@NonNull Message msg, long when) {
+        if ((boolean) sQuitting.getVolatile(this)) {
+            IllegalStateException e = new IllegalStateException(
+                    msg.target + " sending message to a Handler on a dead thread");
+            Log.w(TAG_C, e.getMessage(), e);
+            msg.recycleUnchecked();
+            return false;
+        }
+
+        long seq = when != 0 ? ((long) sNextInsertSeq.getAndAdd(this, 1L) + 1L)
+                : ((long) sNextFrontInsertSeq.getAndAdd(this, -1L) - 1L);
+        /* TODO: Add a MessageNode member to Message so we can avoid this allocation */
+        MessageNode node = new MessageNode(msg, seq);
+        msg.when = when;
+        msg.markInUse();
+
+        if (DEBUG) {
+            Log.d(TAG_C, "Insert message what: " + msg.what + " when: " + msg.when + " seq: "
+                    + node.mInsertSeq + " barrier: " + node.isBarrier() + " async: "
+                    + node.isAsync() + " now: " + SystemClock.uptimeMillis());
+        }
+
+        final Looper myLooper = Looper.myLooper();
+        /* If we are running on the looper thread we can add directly to the priority queue */
+        if (myLooper != null && myLooper.getQueue() == this) {
+            node.removeFromStack();
+            insertIntoPriorityQueue(node);
+            /*
+             * We still need to do this even though we are the current thread,
+             * otherwise next() may sleep indefinitely.
+             */
+            if (!mMessageDirectlyQueued) {
+                mMessageDirectlyQueued = true;
+                nativeWake(mPtr);
+            }
+            return true;
+        }
+
+        while (true) {
+            StackNode old = (StackNode) sState.getVolatile(this);
+            boolean wakeNeeded;
+            boolean inactive;
+
+            node.mNext = old;
+            switch (old.getNodeType()) {
+                case STACK_NODE_ACTIVE:
+                    /*
+                     * The worker thread is currently active and will process any elements added to
+                     * the stack before parking again.
+                     */
+                    node.mBottomOfStack = (StateNode) old;
+                    inactive = false;
+                    node.mWokeUp = true;
+                    wakeNeeded = false;
+                    break;
+
+                case STACK_NODE_PARKED:
+                    node.mBottomOfStack = (StateNode) old;
+                    inactive = true;
+                    node.mWokeUp = true;
+                    wakeNeeded = true;
+                    break;
+
+                case STACK_NODE_TIMEDPARK:
+                    node.mBottomOfStack = (StateNode) old;
+                    inactive = true;
+                    wakeNeeded = mStackStateTimedPark.mWhenToWake >= node.getWhen();
+                    node.mWokeUp = wakeNeeded;
+                    break;
+
+                default:
+                    MessageNode oldMessage = (MessageNode) old;
+
+                    node.mBottomOfStack = oldMessage.mBottomOfStack;
+                    int bottomType = node.mBottomOfStack.getNodeType();
+                    inactive = bottomType >= STACK_NODE_PARKED;
+                    wakeNeeded = (bottomType == STACK_NODE_TIMEDPARK
+                            && mStackStateTimedPark.mWhenToWake >= node.getWhen()
+                            && !oldMessage.mWokeUp);
+                    node.mWokeUp = oldMessage.mWokeUp || wakeNeeded;
+                    break;
+            }
+            if (sState.compareAndSet(this, old, node)) {
+                if (inactive) {
+                    if (wakeNeeded) {
+                        nativeWake(mPtr);
+                    } else {
+                        mMessageCounts.incrementQueued();
+                    }
+                }
+                return true;
+            }
+        }
+    }
+
+    /*
+     * This class is used to find matches for hasMessages() and removeMessages()
+     */
+    private abstract static class MessageCompare {
+        public abstract boolean compareMessage(Message m, Handler h, int what, Object object,
+                Runnable r, long when);
+    }
+
+    private boolean stackHasMessages(Handler h, int what, Object object, Runnable r, long when,
+            MessageCompare compare, boolean removeMatches) {
+        boolean found = false;
+        StackNode top = (StackNode) sState.getVolatile(this);
+        StateNode bottom = getStateNode(top);
+
+        /*
+         * If the top node is a state node, there are no reachable messages.
+         * If it's anything other than Active, we can quit as we know that next() is not
+         * consuming items.
+         * If the top node is Active then we know that next() is currently consuming items.
+         * In that case we should wait next() has drained the stack.
+         */
+        if (top == bottom) {
+            if (bottom != sStackStateActive) {
+                return false;
+            }
+            waitForDrainCompleted();
+            return false;
+        }
+
+        /*
+         * We have messages that we may tombstone. Walk the stack until we hit the bottom or we
+         * hit a null pointer.
+         * If we hit the bottom, we are done.
+         * If we hit a null pointer, then the stack is being consumed by next() and we must cycle
+         * until the stack has been drained.
+         */
+        MessageNode p = (MessageNode) top;
+
+        while (true) {
+            if (compare.compareMessage(p.mMessage, h, what, object, r, when)) {
+                found = true;
+                if (DEBUG) {
+                    Log.d(TAG_C, "stackHasMessages node matches");
+                }
+                if (removeMatches) {
+                    if (p.removeFromStack()) {
+                        p.mMessage.recycleUnchecked();
+                        if (mMessageCounts.incrementCancelled()) {
+                            nativeWake(mPtr);
+                        }
+                    }
+                } else {
+                    return true;
+                }
+            }
+
+            StackNode n = p.mNext;
+            if (n == null) {
+                /* Next() is walking the stack, we must re-sample */
+                if (DEBUG) {
+                    Log.d(TAG_C, "stackHasMessages next() is walking the stack, we must re-sample");
+                }
+                waitForDrainCompleted();
+                break;
+            }
+            if (!n.isMessageNode()) {
+                /* We reached the end of the stack */
+                return found;
+            }
+            p = (MessageNode) n;
+        }
+
+        return found;
+    }
+
+    private boolean priorityQueueHasMessage(ConcurrentSkipListSet<MessageNode> queue, Handler h,
+            int what, Object object, Runnable r, long when, MessageCompare compare,
+            boolean removeMatches) {
+        Iterator<MessageNode> iterator = queue.iterator();
+        boolean found = false;
+
+        while (iterator.hasNext()) {
+            MessageNode msg = iterator.next();
+
+            if (compare.compareMessage(msg.mMessage, h, what, object, r, when)) {
+                if (removeMatches) {
+                    found = true;
+                    if (queue.remove(msg)) {
+                        msg.mMessage.recycleUnchecked();
+                    }
+                } else {
+                    return true;
+                }
+            }
+        }
+        return found;
+    }
+
+    private boolean findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when,
+            MessageCompare compare, boolean removeMatches) {
+        boolean foundInStack, foundInQueue;
+
+        foundInStack = stackHasMessages(h, what, object, r, when, compare, removeMatches);
+        foundInQueue = priorityQueueHasMessage(mPriorityQueue, h, what, object, r, when, compare,
+                removeMatches);
+        foundInQueue |= priorityQueueHasMessage(mAsyncPriorityQueue, h, what, object, r, when,
+                compare, removeMatches);
+
+        return foundInStack || foundInQueue;
+    }
+
+}
diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
index 8eaadde..9db88d1 100644
--- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
+++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java
@@ -382,6 +382,18 @@
         }
     }
 
+    private static final class MatchDeliverableMessages extends MessageCompare {
+        @Override
+        public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
+                long when) {
+            if (m.when <= when) {
+                return true;
+            }
+            return false;
+        }
+    }
+    private final MatchDeliverableMessages mMatchDeliverableMessages =
+            new MatchDeliverableMessages();
     /**
      * Returns true if the looper has no pending messages which are due to be processed.
      *
@@ -390,6 +402,12 @@
      * @return True if the looper is idle.
      */
     public boolean isIdle() {
+        final long now = SystemClock.uptimeMillis();
+
+        if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) {
+            return false;
+        }
+
         MessageNode msgNode = null;
         MessageNode asyncMsgNode = null;
 
@@ -405,7 +423,6 @@
             } catch (NoSuchElementException e) { }
         }
 
-        final long now = SystemClock.uptimeMillis();
         if ((msgNode != null && msgNode.getWhen() <= now)
                 || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) {
             return false;
@@ -967,7 +984,7 @@
         return token;
     }
 
-    private class MatchBarrierToken extends MessageCompare {
+    private static final class MatchBarrierToken extends MessageCompare {
         int mBarrierToken;
 
         MatchBarrierToken(int token) {
@@ -1148,7 +1165,7 @@
         return foundInStack || foundInQueue;
     }
 
-    private static class MatchHandlerWhatAndObject extends MessageCompare {
+    private static final class MatchHandlerWhatAndObject extends MessageCompare {
         @Override
         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
                 long when) {
@@ -1168,7 +1185,7 @@
         return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, false);
     }
 
-    private static class MatchHandlerWhatAndObjectEquals extends MessageCompare {
+    private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
         @Override
         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
                 long when) {
@@ -1189,7 +1206,7 @@
                 false);
     }
 
-    private static class MatchHandlerRunnableAndObject extends MessageCompare {
+    private static final class MatchHandlerRunnableAndObject extends MessageCompare {
         @Override
         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
                 long when) {
@@ -1210,7 +1227,7 @@
         return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, false);
     }
 
-    private static class MatchHandler extends MessageCompare {
+    private static final class MatchHandler extends MessageCompare {
         @Override
         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
                 long when) {
@@ -1249,7 +1266,7 @@
         findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
     }
 
-    private static class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
+    private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
         @Override
         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
                 long when) {
@@ -1268,7 +1285,7 @@
         findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
     }
 
-    private static class MatchHandlerAndObject extends MessageCompare {
+    private static final class MatchHandlerAndObject extends MessageCompare {
         @Override
         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
                 long when) {
@@ -1286,7 +1303,7 @@
         findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true);
     }
 
-    private static class MatchHandlerAndObjectEquals extends MessageCompare {
+    private static final class MatchHandlerAndObjectEquals extends MessageCompare {
         @Override
         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
                 long when) {
@@ -1305,7 +1322,7 @@
         findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
     }
 
-    private static class MatchAllMessages extends MessageCompare {
+    private static final class MatchAllMessages extends MessageCompare {
         @Override
         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
                 long when) {
@@ -1317,7 +1334,7 @@
         findOrRemoveMessages(null, -1, null, null, 0, mMatchAllMessages, true);
     }
 
-    private static class MatchAllFutureMessages extends MessageCompare {
+    private static final class MatchAllFutureMessages extends MessageCompare {
         @Override
         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
                 long when) {
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 89a5e5d..69540c42 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -417,6 +417,14 @@
      */
     @SystemApi
     @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
+    public static @NonNull File getDataSystemDeviceProtectedDirectory() {
+        return buildPath(getDataDirectory(), "system_de");
+    }
+
+    /** Use {@link #getDataSystemDeviceProtectedDirectory()} instead.
+     * {@hide}
+     */
+    @Deprecated
     public static @NonNull File getDataSystemDeDirectory() {
         return buildPath(getDataDirectory(), "system_de");
     }
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/PowerManager.java b/core/java/android/os/PowerManager.java
index 32db3be..9d4ac29 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -517,9 +517,15 @@
     public static final int GO_TO_SLEEP_REASON_DEVICE_FOLD = 13;
 
     /**
+     * Go to sleep reason code: reason unknown.
      * @hide
      */
-    public static final int GO_TO_SLEEP_REASON_MAX =  GO_TO_SLEEP_REASON_DEVICE_FOLD;
+    public static final int GO_TO_SLEEP_REASON_UNKNOWN = 14;
+
+    /**
+     * @hide
+     */
+    public static final int GO_TO_SLEEP_REASON_MAX =  GO_TO_SLEEP_REASON_UNKNOWN;
 
     /**
      * @hide
@@ -540,6 +546,7 @@
             case GO_TO_SLEEP_REASON_QUIESCENT: return "quiescent";
             case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button";
             case GO_TO_SLEEP_REASON_TIMEOUT: return "timeout";
+            case GO_TO_SLEEP_REASON_UNKNOWN: return "unknown";
             default: return Integer.toString(sleepReason);
         }
     }
@@ -635,6 +642,7 @@
             GO_TO_SLEEP_REASON_QUIESCENT,
             GO_TO_SLEEP_REASON_SLEEP_BUTTON,
             GO_TO_SLEEP_REASON_TIMEOUT,
+            GO_TO_SLEEP_REASON_UNKNOWN,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface GoToSleepReason{}
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..90993e1 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -20,17 +20,23 @@
 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.balStrictModeRo;
 
 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.IActivityTaskManager;
+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 +51,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 +89,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 +274,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 +318,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 +913,9 @@
                 if (targetSdk >= Build.VERSION_CODES.S) {
                     detectUnsafeIntentLaunch();
                 }
+                if (balStrictModeRo() && 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 +1154,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_RO)
+            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_RO)
+            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 +2180,28 @@
                 registerIntentMatchingRestrictionCallback();
             }
 
+            if ((sVmPolicy.mask & DETECT_VM_BACKGROUND_ACTIVITY_LAUNCH_ABORTED) != 0) {
+                registerBackgroundActivityLaunchCallback();
+            }
+
             setBlockGuardVmPolicy(sVmPolicy.mask);
         }
     }
 
+    private static void registerBackgroundActivityLaunchCallback() {
+        try {
+            IActivityTaskManager service = ActivityTaskManager.getService();
+            if (service != null) {
+                service.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 +2226,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 +2311,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 +2482,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/UserManager.java b/core/java/android/os/UserManager.java
index fa99f35..4bc8fe0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -5502,10 +5502,14 @@
             Manifest.permission.CREATE_USERS,
             Manifest.permission.QUERY_USERS}, conditional = true)
     public @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly) {
-        try {
-            return mService.getProfileIds(userId, enabledOnly);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
+        if (android.multiuser.Flags.cacheProfileIdsReadOnly()) {
+            return enabledOnly ? getEnabledProfileIds(userId) : getProfileIdsWithDisabled(userId);
+        } else {
+            try {
+                return mService.getProfileIds(userId, enabledOnly);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
         }
     }
 
@@ -5518,8 +5522,14 @@
             Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS,
             Manifest.permission.QUERY_USERS}, conditional = true)
+    @CachedProperty(api = "user_manager_users")
     public int[] getProfileIdsWithDisabled(@UserIdInt int userId) {
-        return getProfileIds(userId, false /* enabledOnly */);
+        if (android.multiuser.Flags.cacheProfileIdsReadOnly()) {
+            return UserManagerCache.getProfileIdsWithDisabled(
+                (Integer userIdentifuer) -> mService.getProfileIds(userIdentifuer, false), userId);
+        } else {
+            return getProfileIds(userId, false /* enabledOnly */);
+        }
     }
 
     /**
@@ -5530,8 +5540,21 @@
             Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS,
             Manifest.permission.QUERY_USERS}, conditional = true)
+    @CachedProperty(api = "user_manager_users_enabled")
     public int[] getEnabledProfileIds(@UserIdInt int userId) {
-        return getProfileIds(userId, true /* enabledOnly */);
+        if (android.multiuser.Flags.cacheProfileIdsReadOnly()) {
+            return UserManagerCache.getEnabledProfileIds(
+                (Integer userIdentifuer) -> mService.getProfileIds(userIdentifuer, true), userId);
+        } else {
+            return getProfileIds(userId, true /* enabledOnly */);
+        }
+    }
+
+    /** @hide */
+    public static final void invalidateEnabledProfileIds() {
+        if (android.multiuser.Flags.cacheProfileIdsReadOnly()) {
+            UserManagerCache.invalidateEnabledProfileIds();
+        }
     }
 
     /**
@@ -6443,6 +6466,21 @@
         if (android.multiuser.Flags.cacheProfileParentReadOnly()) {
             UserManagerCache.invalidateProfileParent();
         }
+        invalidateEnabledProfileIds();
+    }
+
+    /**
+     * Invalidate caches when related to specific user info flags change.
+     *
+     * @param flag a combination of FLAG_ constants, from the list in
+     *        {@link UserInfo#UserInfoFlag}, whose value has changed and the associated
+     *        invalidations must therefore be performed.
+     * @hide
+     */
+    public static final void invalidateOnUserInfoFlagChange(@UserInfoFlag int flags) {
+        if ((flags & UserInfo.FLAG_DISABLED) > 0) {
+            invalidateEnabledProfileIds();
+        }
     }
 
     /**
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index d8094ab..0cffd9f 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -1404,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}.
      *
@@ -1697,7 +1740,9 @@
      *
      * <p>The waveform envelope builder offers more flexibility for creating waveform effects,
      * allowing control over vibration amplitude and frequency via smooth transitions between
-     * values.
+     * values. The waveform will start the first transition from the vibrator off state, using
+     * the same frequency of the first control point. To provide a different initial vibration
+     * frequency, use {@link #startWaveformEnvelope(float)}.
      *
      * <p>Note: To check whether waveform envelope effects are supported, use
      * {@link Vibrator#areEnvelopeEffectsSupported()}.
@@ -1711,6 +1756,32 @@
     }
 
     /**
+     * Start building a waveform vibration with an initial frequency.
+     *
+     * <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>This is the same as {@link #startWaveformEnvelope()}, but the waveform will start
+     * vibrating at given frequency, in hertz, while it transitions to the new amplitude and
+     * frequency of the first control point.
+     *
+     * <p>Note: To check whether waveform envelope effects are supported, use
+     * {@link Vibrator#areEnvelopeEffectsSupported()}.
+     *
+     * @param initialFrequencyHz The starting frequency of the vibration, in hertz. Must be greater
+     *                           than zero.
+     *
+     * @see VibrationEffect.WaveformEnvelopeBuilder
+     */
+    @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    @NonNull
+    public static VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope(
+            @FloatRange(from = 0) float initialFrequencyHz) {
+        return new WaveformEnvelopeBuilder(initialFrequencyHz);
+    }
+
+    /**
      * A builder for waveform effects described by its envelope.
      *
      * <p>Waveform effect envelopes are defined by one or more control points describing a target
@@ -1767,6 +1838,10 @@
 
         private WaveformEnvelopeBuilder() {}
 
+        private WaveformEnvelopeBuilder(float initialFrequency) {
+            mLastFrequencyHz = initialFrequency;
+        }
+
         /**
          * Adds a new control point to the end of this waveform envelope.
          *
@@ -1798,7 +1873,7 @@
                 @FloatRange(from = 0, to = 1) float amplitude,
                 @FloatRange(from = 0) float frequencyHz, int timeMillis) {
 
-            if (mSegments.isEmpty()) {
+            if (mLastFrequencyHz == 0) {
                 mLastFrequencyHz = frequencyHz;
             }
 
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/PwlePoint.java b/core/java/android/os/vibrator/PwlePoint.java
new file mode 100644
index 0000000..ea3ae6c
--- /dev/null
+++ b/core/java/android/os/vibrator/PwlePoint.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.vibrator;
+
+import java.util.Objects;
+
+/**
+ * A {@link PwlePoint} represents a single point in an envelope vibration effect. Defined by its
+ * amplitude, frequency and time to transition to this point from the previous one in the envelope.
+ *
+ * @hide
+ */
+public final class PwlePoint {
+    private final float mAmplitude;
+    private final float mFrequencyHz;
+    private final int mTimeMillis;
+
+    /** @hide */
+    public PwlePoint(float amplitude, float frequencyHz, int timeMillis) {
+        mAmplitude = amplitude;
+        mFrequencyHz = frequencyHz;
+        mTimeMillis = timeMillis;
+    }
+
+    public float getAmplitude() {
+        return mAmplitude;
+    }
+
+    public float getFrequencyHz() {
+        return mFrequencyHz;
+    }
+
+    public int getTimeMillis() {
+        return mTimeMillis;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof PwlePoint)) {
+            return false;
+        }
+        PwlePoint other = (PwlePoint) obj;
+        return Float.compare(mAmplitude, other.mAmplitude) == 0
+                && Float.compare(mFrequencyHz, other.mFrequencyHz) == 0
+                && mTimeMillis == other.mTimeMillis;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAmplitude, mFrequencyHz, mTimeMillis);
+    }
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 1d654e13..6a49322 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -239,6 +239,13 @@
 }
 
 flag {
+  name: "use_frozen_aware_remote_callback_list"
+  namespace: "permissions"
+  description: "Whether to use the new frozen-aware RemoteCallbackList API for op noted callbacks."
+  bug: "361157077"
+}
+
+flag {
     name: "wallet_role_icon_property_enabled"
     is_exported: true
     namespace: "wallet_integration"
@@ -307,3 +314,21 @@
     description: "Enable AppOp mode caching in AppOpsManager"
     bug: "366013082"
 }
+
+flag {
+    name: "permission_tree_apis_deprecated"
+    is_fixed_read_only: true
+    is_exported: true
+    namespace: "permissions"
+    description: "This flag is used to deprecate permission tree related APIs"
+    bug: "376535612"
+}
+
+flag {
+    name: "enable_otp_in_text_classifiers"
+    is_fixed_read_only: true
+    is_exported: true
+    namespace: "permissions"
+    description: "Enables ExtServices to leverage TextClassifier for OTP detection"
+    bug: "351976749"
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 8afc177..1b289fd 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -9383,7 +9383,14 @@
          * @param resolver the ContentResolver to query.
          * @return the default account for new contacts, or null if it's not set or set to NULL
          * account.
+         *
+         * @deprecated This API is only supported up to Android version
+         *      * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. On later versions,
+         * {@link ContactsContract.RawContacts.DefaultAccount#getDefaultAccountForNewContacts}
+         * should be used.
          */
+        @Deprecated
+        @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED)
         @Nullable
         public static Account getDefaultAccount(@NonNull ContentResolver resolver) {
             Bundle response = resolver.call(ContactsContract.AUTHORITY_URI,
@@ -9404,7 +9411,14 @@
          * @param resolver the ContentResolver to query.
          * @param account the account to be set to default.
          * @hide
+         *
+         * @deprecated This API is only supported up to Android version
+         *      * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}. On later versions,
+         * {@link ContactsContract.RawContacts.DefaultAccount#setDefaultAccountForNewContacts}
+         * should be used.
          */
+        @Deprecated
+        @FlaggedApi(Flags.FLAG_NEW_DEFAULT_ACCOUNT_API_ENABLED)
         @SystemApi
         @RequiresPermission(android.Manifest.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS)
         public static void setDefaultAccount(@NonNull ContentResolver resolver,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8efbc9c..d19681c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6332,6 +6332,27 @@
         public static final String SCREEN_FLASH_NOTIFICATION_COLOR =
                 "screen_flash_notification_color_global";
 
+
+        /**
+         * A semi-colon separated list of Bluetooth hearing devices' local ambient volume.
+         * Each entry is encoded as a key=value list, separated by commas. Ex:
+         *
+         * "addr=XX:XX:XX:00:11,ambient=20,group_ambient=30;addr=XX:XX:XX:00:22,ambient=50"
+         *
+         * The following keys are supported:
+         * <pre>
+         * addr                 (String)
+         * ambient              (int)
+         * group_ambient        (int)
+         * control_expanded     (boolean)
+         * </pre>
+         *
+         * Each entry must contains "addr" attribute, otherwise it'll be ignored.
+         * @hide
+         */
+        public static final String HEARING_DEVICE_LOCAL_AMBIENT_VOLUME =
+                "hearing_device_local_ambient_volume";
+
         /**
          * IMPORTANT: If you add a new public settings you also have to add it to
          * PUBLIC_SETTINGS below. If the new setting is hidden you have to add
@@ -6476,6 +6497,7 @@
             PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE);
             PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING);
             PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON);
+            PRIVATE_SETTINGS.add(HEARING_DEVICE_LOCAL_AMBIENT_VOLUME);
         }
 
         /**
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.aidl
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to core/java/android/security/advancedprotection/AdvancedProtectionFeature.aidl
index 83cbc52..3ecef02 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.aidl
@@ -13,10 +13,11 @@
  * 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.security.advancedprotection;
+
+/**
+ * Represents an advanced protection feature providing protections
+ * @hide
+ */
+parcelable AdvancedProtectionFeature;
\ No newline at end of file
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java
new file mode 100644
index 0000000..a086bf7
--- /dev/null
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionFeature.java
@@ -0,0 +1,77 @@
+/*
+ * 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.security.advancedprotection;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.security.Flags;
+
+/**
+ * An advanced protection feature providing protections.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_AAPM_API)
+@SystemApi
+public final class AdvancedProtectionFeature implements Parcelable {
+    private final String mId;
+
+    /**
+     * Create an object identifying an Advanced Protection feature for AdvancedProtectionManager
+     * @param id A unique ID to identify this feature. It is used by Settings screens to display
+     *           information about this feature.
+     */
+    public AdvancedProtectionFeature(@NonNull String id) {
+        mId = id;
+    }
+
+    private AdvancedProtectionFeature(Parcel in) {
+        mId = in.readString8();
+    }
+
+    /**
+     * @return the unique ID representing this feature
+     */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString8(mId);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<AdvancedProtectionFeature> CREATOR =
+            new Parcelable.Creator<>() {
+                public AdvancedProtectionFeature createFromParcel(Parcel in) {
+                    return new AdvancedProtectionFeature(in);
+                }
+
+                public AdvancedProtectionFeature[] newArray(int size) {
+                    return new AdvancedProtectionFeature[size];
+                }
+            };
+}
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 59dd680f..6f3e3d8 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -29,6 +29,7 @@
 import android.security.Flags;
 import android.util.Log;
 
+import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 
@@ -41,8 +42,8 @@
  */
 @FlaggedApi(Flags.FLAG_AAPM_API)
 @SystemService(Context.ADVANCED_PROTECTION_SERVICE)
-public class AdvancedProtectionManager {
-    private static final String TAG = "AdvancedProtectionM";
+public final class AdvancedProtectionManager {
+    private static final String TAG = "AdvancedProtectionMgr";
 
     private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback>
             mCallbackMap = new ConcurrentHashMap<>();
@@ -73,7 +74,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.
@@ -147,6 +148,22 @@
     }
 
     /**
+     * Returns the list of advanced protection features which are available on this device.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @RequiresPermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE)
+    public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() {
+        try {
+            return mService.getAdvancedProtectionFeatures();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * A callback class for monitoring changes to Advanced Protection state
      *
      * <p>To register a callback, implement this interface, and register it with
diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
index ef0abf4..6830763 100644
--- a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
+++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl
@@ -16,6 +16,7 @@
 
 package android.security.advancedprotection;
 
+import android.security.advancedprotection.AdvancedProtectionFeature;
 import android.security.advancedprotection.IAdvancedProtectionCallback;
 
 /**
@@ -32,4 +33,6 @@
     void unregisterAdvancedProtectionCallback(IAdvancedProtectionCallback callback);
     @EnforcePermission("SET_ADVANCED_PROTECTION_MODE")
     void setAdvancedProtectionEnabled(boolean enabled);
+    @EnforcePermission("SET_ADVANCED_PROTECTION_MODE")
+    List<AdvancedProtectionFeature> getAdvancedProtectionFeatures();
 }
\ No newline at end of file
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index dec28c3..5995760 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -87,6 +87,14 @@
 }
 
 flag {
+    name: "prevent_intent_redirect_show_toast"
+    namespace: "responsible_apis"
+    description: "Prevent intent redirect attacks by showing a toast when activity start is blocked"
+    bug: "361143368"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "enable_intent_matching_flags"
     is_exported: true
     namespace: "permissions"
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/contextualsearch/OWNERS b/core/java/android/service/contextualsearch/OWNERS
index b723872..c435bd8 100644
--- a/core/java/android/service/contextualsearch/OWNERS
+++ b/core/java/android/service/contextualsearch/OWNERS
@@ -1,2 +1,3 @@
 srazdan@google.com
-hackz@google.com
+hyunyoungs@google.com
+awickham@google.com
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index bd9ab86..a8ab211 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -17,6 +17,7 @@
 package android.service.notification;
 
 import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -882,6 +883,35 @@
         }
     }
 
+    /**
+     * Creates a conversation notification channel for a given package for a given user.
+     *
+     * <p>This method will throw a security exception if you don't have access to notifications
+     * for the given user.</p>
+     * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
+     * device} or be the notification assistant in order to use this method.
+     *
+     * @param pkg The package the channel belongs to.
+     * @param user The user the channel belongs to.
+     * @param parentChannelId The parent channel id of the conversation channel belongs to.
+     * @param conversationId The conversation id of the conversation channel.
+     *
+     * @return The created conversation channel.
+     */
+    @FlaggedApi(Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+    public final @Nullable NotificationChannel createConversationNotificationChannelForPackage(
+        @NonNull String pkg, @NonNull UserHandle user, @NonNull String parentChannelId,
+        @NonNull String conversationId) {
+        if (!isBound()) return null;
+        try {
+            return getNotificationInterface()
+                    .createConversationNotificationChannelForPackageFromPrivilegedListener(
+                            mWrapper, pkg, user, parentChannelId, conversationId);
+        } catch (RemoteException e) {
+            Log.v(TAG, "Unable to contact notification manager", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * Updates a notification channel for a given package for a given user. This should only be used
@@ -890,7 +920,7 @@
      * <p>This method will throw a security exception if you don't have access to notifications
      * for the given user.</p>
      * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
-     * device} in order to use this method.
+     * device} or be the notification assistant in order to use this method.
      *
      * @param pkg The package the channel belongs to.
      * @param user The user the channel belongs to.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index e173255..24328eb 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -24,12 +24,12 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
 import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders;
 import static android.service.notification.ZenAdapters.prioritySendersToPeopleType;
 import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy;
-import static android.service.notification.ZenModeConfig.EventInfo.REPLY_YES_OR_MAYBE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS;
@@ -56,6 +56,7 @@
 import android.app.Flags;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
+import android.app.backup.BackupRestoreEventLogger;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -957,8 +958,9 @@
         }
     }
 
-    public static ZenModeConfig readXml(TypedXmlPullParser parser)
-            throws XmlPullParserException, IOException {
+    public static ZenModeConfig readXml(TypedXmlPullParser parser,
+            @Nullable BackupRestoreEventLogger logger) throws XmlPullParserException, IOException {
+        int readRuleCount = 0;
         int type = parser.getEventType();
         if (type != XmlPullParser.START_TAG) return null;
         String tag = parser.getName();
@@ -1048,6 +1050,8 @@
                     readManualRule = true;
                     if (rt.manualRule.zenPolicy == null) {
                         readManualRuleWithoutPolicy = true;
+                    } else {
+                        readRuleCount++;
                     }
                 } else if (AUTOMATIC_TAG.equals(tag)
                         || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
@@ -1062,6 +1066,7 @@
                             }
                         } else if (AUTOMATIC_TAG.equals(tag)) {
                             rt.automaticRules.put(id, automaticRule);
+                            readRuleCount++;
                         }
                     }
                 } else if (STATE_TAG.equals(tag)) {
@@ -1085,8 +1090,17 @@
                         }
                         rt.manualRule.condition = new Condition(rt.manualRule.conditionId, "",
                                 Condition.STATE_TRUE);
+                        readRuleCount++;
                     }
                 }
+
+                if (!Flags.modesUi()){
+                    readRuleCount++;
+                }
+
+                if (logger != null) {
+                    logger.logItemsRestored(DATA_TYPE_ZEN_RULES, readRuleCount);
+                }
                 return rt;
             }
         }
@@ -1110,8 +1124,9 @@
      * @throws IOException
      */
 
-    public void writeXml(TypedXmlSerializer out, Integer version, boolean forBackup)
-            throws IOException {
+    public void writeXml(TypedXmlSerializer out, Integer version, boolean forBackup,
+            @Nullable BackupRestoreEventLogger logger) throws IOException {
+        int writtenRuleCount = 0;
         int xmlVersion = getCurrentXmlVersion();
         out.startTag(null, ZEN_TAG);
         out.attribute(null, ZEN_ATT_VERSION, version == null
@@ -1147,6 +1162,7 @@
             writeRuleXml(manualRule, out, forBackup);
             out.endTag(null, MANUAL_TAG);
         }
+        writtenRuleCount++;
         final int N = automaticRules.size();
         for (int i = 0; i < N; i++) {
             final String id = automaticRules.keyAt(i);
@@ -1155,6 +1171,7 @@
             out.attribute(null, RULE_ATT_ID, id);
             writeRuleXml(automaticRule, out, forBackup);
             out.endTag(null, AUTOMATIC_TAG);
+            writtenRuleCount++;
         }
         if (Flags.modesApi() && !forBackup) {
             for (int i = 0; i < deletedRules.size(); i++) {
@@ -1171,6 +1188,9 @@
         out.endTag(null, STATE_TAG);
 
         out.endTag(null, ZEN_TAG);
+        if (logger != null) {
+            logger.logItemsBackedUp(DATA_TYPE_ZEN_RULES, writtenRuleCount);
+        }
     }
 
     @NonNull
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 51961a8..34e311f 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -57,4 +57,12 @@
   namespace: "systemui"
   description: "Guards the new FLAG_SILENT Notification flag"
   bug: "336488844"
+}
+
+flag {
+   name: "notification_conversation_channel_management"
+   is_exported: true
+   namespace: "systemui"
+   description: "Allows the NAS to create and modify conversation notifications"
+   bug: "373599715"
 }
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index f1ae22e..e9e2646 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -22,6 +22,7 @@
 import android.graphics.RectF;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
+import android.app.wallpaper.WallpaperDescription;
 import android.os.Bundle;
 
 /**
@@ -50,4 +51,5 @@
     SurfaceControl mirrorSurfaceControl();
     oneway void applyDimming(float dimAmount);
     oneway void setWallpaperFlags(int which);
+    @nullable WallpaperDescription onApplyWallpaper(int which);
 }
diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
index 3262f3a..f76e6ce 100644
--- a/core/java/android/service/wallpaper/IWallpaperService.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -17,6 +17,7 @@
 package android.service.wallpaper;
 
 import android.app.WallpaperInfo;
+import android.app.wallpaper.WallpaperDescription;
 import android.graphics.Rect;
 import android.service.wallpaper.IWallpaperConnection;
 
@@ -27,6 +28,6 @@
     void attach(IWallpaperConnection connection,
             IBinder windowToken, int windowType, boolean isPreview,
             int reqWidth, int reqHeight, in Rect padding, int displayId, int which,
-            in WallpaperInfo info);
+            in WallpaperInfo info, in @nullable WallpaperDescription description);
     void detach(IBinder windowToken);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 2ab16e9..131fdc8 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -16,6 +16,7 @@
 
 package android.service.wallpaper;
 
+import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
 import static android.app.WallpaperManager.COMMAND_FREEZE;
 import static android.app.WallpaperManager.COMMAND_UNFREEZE;
 import static android.app.WallpaperManager.SetWallpaperFlags;
@@ -50,6 +51,7 @@
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
 import android.app.compat.CompatChanges;
+import android.app.wallpaper.WallpaperDescription;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.EnabledSince;
@@ -221,14 +223,14 @@
 
     /**
      * Wear products currently force a slight scaling transition to wallpapers
-     * when the QSS is opened. However, on Wear 6 (SDK 35) and above, 1P watch faces
+     * when the QSS is opened. However, on Wear 7 (SDK 37) and above, 1P watch faces
      * will be expected to either implement their own scaling, or to override this
      * method to allow the WallpaperController to continue to scale for them.
      *
      * @hide
      */
     @ChangeId
-    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
     public static final long WEAROS_WALLPAPER_HANDLES_SCALING = 272527315L;
 
     static final class WallpaperCommand {
@@ -922,6 +924,24 @@
         }
 
         /**
+         * Called when the wallpaper preview rendered by this engine is about to be persisted as
+         * a selected wallpaper. The returned WallpaperDescription (if any) will be persisted by
+         * the system and passed into subsequent calls to
+         * {@link WallpaperService#onCreateEngine(WallpaperDescription)}. This allows the Engine
+         * to perform any necessary bookkeeping before a wallpaper being previewed is set on
+         * the device, and update the description if necessary.
+         *
+         * @param which Specifies wallpaper destination: home, lock, or both
+         * @return the description of the applied wallpaper, or {@code null} if description is
+         * unchanged
+         */
+        @Nullable
+        @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+        public WallpaperDescription onApplyWallpaper(@SetWallpaperFlags int which) {
+            return null;
+        }
+
+        /**
          * Notifies the engine that wallpaper colors changed significantly.
          * This will trigger a {@link #onComputeColors()} call.
          */
@@ -2449,6 +2469,7 @@
         final Display mDisplay;
         final WallpaperManager mWallpaperManager;
         @Nullable final WallpaperInfo mInfo;
+        @NonNull final WallpaperDescription mDescription;
 
         Engine mEngine;
         @SetWallpaperFlags int mWhich;
@@ -2456,7 +2477,8 @@
         IWallpaperEngineWrapper(WallpaperService service,
                 IWallpaperConnection conn, IBinder windowToken,
                 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
-                int displayId, @SetWallpaperFlags int which, @Nullable WallpaperInfo info) {
+                int displayId, @SetWallpaperFlags int which, @Nullable WallpaperInfo info,
+                @NonNull WallpaperDescription description) {
             mWallpaperManager = getSystemService(WallpaperManager.class);
             mCaller = new HandlerCaller(service, service.onProvideEngineLooper(), this, true);
             mConnection = conn;
@@ -2469,6 +2491,7 @@
             mDisplayId = displayId;
             mWhich = which;
             mInfo = info;
+            mDescription = description;
 
             // Create a display context before onCreateEngine.
             mDisplayManager = getSystemService(DisplayManager.class);
@@ -2593,9 +2616,19 @@
             return mEngine == null ? null : SurfaceControl.mirrorSurface(mEngine.mSurfaceControl);
         }
 
+        @Nullable
+        public WallpaperDescription onApplyWallpaper(@SetWallpaperFlags int which) {
+            return mEngine != null ? mEngine.onApplyWallpaper(which) : null;
+        }
+
         private void doAttachEngine() {
             Trace.beginSection("WPMS.onCreateEngine");
-            Engine engine = onCreateEngine();
+            Engine engine;
+            if (mDescription != null) {
+                engine = onCreateEngine(mDescription);
+            } else {
+                engine = onCreateEngine();
+            }
             Trace.endSection();
             mEngine = engine;
             Trace.beginSection("WPMS.mConnection.attachEngine-" + mDisplayId);
@@ -2804,11 +2837,13 @@
         @Override
         public void attach(IWallpaperConnection conn, IBinder windowToken,
                 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
-                int displayId, @SetWallpaperFlags int which, @Nullable WallpaperInfo info) {
+                int displayId, @SetWallpaperFlags int which, WallpaperInfo info,
+                @NonNull WallpaperDescription description) {
             Trace.beginSection("WPMS.ServiceWrapper.attach");
             IWallpaperEngineWrapper engineWrapper =
                     new IWallpaperEngineWrapper(mTarget, conn, windowToken, windowType,
-                            isPreview, reqWidth, reqHeight, padding, displayId, which, info);
+                            isPreview, reqWidth, reqHeight, padding, displayId, which, info,
+                            description);
             synchronized (mActiveEngines) {
                 mActiveEngines.put(windowToken, engineWrapper);
             }
@@ -2883,6 +2918,19 @@
     @MainThread
     public abstract Engine onCreateEngine();
 
+    /**
+     * Creates a new engine instance to show the given content. See also {@link #onCreateEngine()}.
+     *
+     * @param description content to display
+     * @return the rendering engine
+     */
+    @FlaggedApi(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+    @MainThread
+    @Nullable
+    public Engine onCreateEngine(@NonNull WallpaperDescription description) {
+        return onCreateEngine();
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter out, String[] args) {
         out.print("State of wallpaper "); out.print(this); out.println(":");
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/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 4d50a45..1dab2cf 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -47,6 +47,8 @@
 import android.telephony.ims.ImsCallSession;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.MediaQualityStatus;
+import android.telephony.satellite.SatelliteStateChangeListener;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -55,12 +57,14 @@
 import com.android.internal.telephony.ICarrierConfigChangeListener;
 import com.android.internal.telephony.ICarrierPrivilegesCallback;
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
+import com.android.internal.telephony.ISatelliteStateChangeListener;
 import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.server.telecom.flags.Flags;
 
 import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.WeakHashMap;
@@ -1482,6 +1486,111 @@
                 pkgName, attributionTag, callback, new int[0], notifyNow);
     }
 
+    @NonNull
+    @GuardedBy("sSatelliteStateChangeListeners")
+    private static final Map<SatelliteStateChangeListener,
+                WeakReference<SatelliteStateChangeListenerWrapper>>
+            sSatelliteStateChangeListeners = new ArrayMap<>();
+
+    /**
+     * Register a {@link SatelliteStateChangeListener} to receive notification when Satellite state
+     * has changed.
+     *
+     * @param executor The {@link Executor} where the {@code listener} will be invoked
+     * @param listener The listener to monitor the satellite state change
+     * @hide
+     */
+    public void addSatelliteStateChangeListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteStateChangeListener listener) {
+        if (listener == null || executor == null) {
+            throw new IllegalArgumentException("Listener and executor must be non-null");
+        }
+
+        synchronized (sSatelliteStateChangeListeners) {
+            WeakReference<SatelliteStateChangeListenerWrapper> existing =
+                    sSatelliteStateChangeListeners.get(listener);
+            if (existing != null && existing.get() != null) {
+                Log.d(TAG, "addSatelliteStateChangeListener: listener already registered");
+                return;
+            }
+            SatelliteStateChangeListenerWrapper wrapper =
+                    new SatelliteStateChangeListenerWrapper(executor, listener);
+            try {
+                sRegistry.addSatelliteStateChangeListener(
+                        wrapper,
+                        mContext.getOpPackageName(),
+                        mContext.getAttributionTag());
+                sSatelliteStateChangeListeners.put(listener, new WeakReference<>(wrapper));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Unregister a {@link SatelliteStateChangeListener} to stop receiving notification when
+     * satellite state has changed.
+     *
+     * @param listener The listener previously registered with addSatelliteStateChangeListener.
+     * @hide
+     */
+    public void removeSatelliteStateChangeListener(@NonNull SatelliteStateChangeListener listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must be non-null");
+        }
+
+        synchronized (sSatelliteStateChangeListeners) {
+            WeakReference<SatelliteStateChangeListenerWrapper> ref =
+                    sSatelliteStateChangeListeners.get(listener);
+            if (ref == null) return;
+            SatelliteStateChangeListenerWrapper wrapper = ref.get();
+            if (wrapper == null) return;
+            try {
+                sRegistry.removeSatelliteStateChangeListener(wrapper, mContext.getOpPackageName());
+                sSatelliteStateChangeListeners.remove(listener);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Notify the registrants that the satellite state has changed.
+     *
+     * @param isEnabled True if the satellite modem is enabled, false otherwise
+     * @hide
+     */
+    public void notifySatelliteStateChanged(boolean isEnabled) {
+        try {
+            sRegistry.notifySatelliteStateChanged(isEnabled);
+        } catch (RemoteException ex) {
+            // system process is dead
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    private static class SatelliteStateChangeListenerWrapper extends
+            ISatelliteStateChangeListener.Stub implements ListenerExecutor {
+        @NonNull private final WeakReference<SatelliteStateChangeListener> mListener;
+        @NonNull private final Executor mExecutor;
+
+        SatelliteStateChangeListenerWrapper(@NonNull Executor executor,
+                @NonNull SatelliteStateChangeListener listener) {
+            mExecutor = executor;
+            mListener = new WeakReference<>(listener);
+        }
+
+        @Override
+        public void onSatelliteEnabledStateChanged(boolean isEnabled) {
+            Binder.withCleanCallingIdentity(
+                    () ->
+                            executeSafely(
+                                    mExecutor,
+                                    mListener::get,
+                                    sscl -> sscl.onEnabledStateChanged(isEnabled)));
+        }
+    }
+
     private static class CarrierPrivilegesCallbackWrapper extends ICarrierPrivilegesCallback.Stub
             implements ListenerExecutor {
         @NonNull private final WeakReference<CarrierPrivilegesCallback> mCallback;
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 09b2201..e830d89 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -195,3 +195,10 @@
   description: "Feature flag for adding a TYPE_DURATION to TtsSpan"
   bug: "337103893"
 }
+
+flag {
+  name: "deprecate_elegant_text_height_api"
+  namespace: "text"
+  description: "Deprecate the Paint#elegantTextHeight API and stick it to true"
+  bug: "349519475"
+}
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/Display.java b/core/java/android/view/Display.java
index e940e55bd..910e644 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -22,6 +22,7 @@
 
 import static com.android.server.display.feature.flags.Flags.FLAG_HIGHEST_HDR_SDR_RATIO_API;
 import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_HAS_ARR_SUPPORT;
+import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE;
 
 import android.Manifest;
 import android.annotation.FlaggedApi;
@@ -1278,6 +1279,60 @@
         }
     }
 
+    @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
+    public static final int FRAME_RATE_CATEGORY_NORMAL = 0;
+    @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
+    public static final int FRAME_RATE_CATEGORY_HIGH = 1;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = {"FRAME_RATE_CATEGORY_"},
+            value = {
+                FRAME_RATE_CATEGORY_NORMAL,
+                FRAME_RATE_CATEGORY_HIGH
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrameRateCategory {}
+
+    /**
+     * <p> Gets the display-defined frame rate given a descriptive frame rate category. </p>
+     *
+     * <p> For example, an animation that does not require fast render rates can use
+     * the {@link #FRAME_RATE_CATEGORY_NORMAL} to get the suggested frame rate.
+     * The suggested frame rate then can be used in the
+     * {@link Surface.FrameRateParams.Builder#setDesiredRateRange} for desiredMinRate.
+     *
+     * <pre>{@code
+     *  float desiredMinRate = display.getSuggestedFrameRate(FRAME_RATE_CATEGORY_NORMAL);
+     *  Surface.FrameRateParams params = new Surface.FrameRateParams.Builder().
+     *                                      setDesiredRateRange(desiredMinRate, Float.MAX).build();
+     *  surface.setFrameRate(params);
+     * }</pre>
+     * </p>
+     *
+     * @param category either {@link #FRAME_RATE_CATEGORY_NORMAL}
+     *                 or {@link #FRAME_RATE_CATEGORY_HIGH}
+     *
+     * @see Surface#setFrameRate(Surface.FrameRateParams)
+     * @see SurfaceControl.Transaction#setFrameRate(SurfaceControl, Surface.FrameRateParams)
+     * @throws IllegalArgumentException when category is not {@link #FRAME_RATE_CATEGORY_NORMAL}
+     * or {@link #FRAME_RATE_CATEGORY_HIGH}
+     */
+    @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE)
+    public float getSuggestedFrameRate(@FrameRateCategory int category) {
+        synchronized (mLock) {
+            updateDisplayInfoLocked();
+            if (category == FRAME_RATE_CATEGORY_HIGH) {
+                return mDisplayInfo.frameRateCategoryRate.getHigh();
+            } else if (category == FRAME_RATE_CATEGORY_NORMAL) {
+                return mDisplayInfo.frameRateCategoryRate.getNormal();
+            } else {
+                throw new IllegalArgumentException("Invalid FrameRateCategory provided");
+            }
+        }
+    }
+
     /**
      * <p> Returns true if the connected display can be switched into a mode with minimal
      * post processing. </p>
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 26fce90..8f112f3 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -204,6 +204,12 @@
     public boolean hasArrSupport;
 
     /**
+     * Represents frame rate for the FrameRateCategory Normal and High.
+     * @see android.view.Display#getSuggestedFrameRate(int) for more details.
+     */
+    public FrameRateCategoryRate frameRateCategoryRate;
+
+    /**
      * The default display mode.
      */
     public int defaultModeId;
@@ -443,6 +449,7 @@
                 && modeId == other.modeId
                 && renderFrameRate == other.renderFrameRate
                 && hasArrSupport == other.hasArrSupport
+                && Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate)
                 && defaultModeId == other.defaultModeId
                 && userPreferredModeId == other.userPreferredModeId
                 && Arrays.equals(supportedModes, other.supportedModes)
@@ -505,6 +512,7 @@
         modeId = other.modeId;
         renderFrameRate = other.renderFrameRate;
         hasArrSupport = other.hasArrSupport;
+        frameRateCategoryRate = other.frameRateCategoryRate;
         defaultModeId = other.defaultModeId;
         userPreferredModeId = other.userPreferredModeId;
         supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length);
@@ -562,6 +570,8 @@
         modeId = source.readInt();
         renderFrameRate = source.readFloat();
         hasArrSupport = source.readBoolean();
+        frameRateCategoryRate = source.readParcelable(null,
+                android.view.FrameRateCategoryRate.class);
         defaultModeId = source.readInt();
         userPreferredModeId = source.readInt();
         int nModes = source.readInt();
@@ -636,6 +646,7 @@
         dest.writeInt(modeId);
         dest.writeFloat(renderFrameRate);
         dest.writeBoolean(hasArrSupport);
+        dest.writeParcelable(frameRateCategoryRate, flags);
         dest.writeInt(defaultModeId);
         dest.writeInt(userPreferredModeId);
         dest.writeInt(supportedModes.length);
@@ -883,6 +894,8 @@
         sb.append(renderFrameRate);
         sb.append(", hasArrSupport ");
         sb.append(hasArrSupport);
+        sb.append(", frameRateCategoryRate ");
+        sb.append(frameRateCategoryRate);
         sb.append(", defaultMode ");
         sb.append(defaultModeId);
         sb.append(", userPreferredModeId ");
diff --git a/core/java/android/view/FrameRateCategoryRate.java b/core/java/android/view/FrameRateCategoryRate.java
new file mode 100644
index 0000000..3c674b8
--- /dev/null
+++ b/core/java/android/view/FrameRateCategoryRate.java
@@ -0,0 +1,113 @@
+/*
+ * 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.view;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class to create and manage FrameRateCategoryRate for
+ * categories Normal and High.
+ * @hide
+ */
+public class FrameRateCategoryRate implements Parcelable {
+
+    private final float mNormal;
+    private final float mHigh;
+
+    /**
+     * Creates a FrameRateCategoryRate with the provided rates
+     * for the categories Normal and High respectively;
+     *
+     * @param normal rate for the category Normal.
+     * @param high rate for the category High.
+     * @hide
+     */
+    public FrameRateCategoryRate(float normal, float high) {
+        this.mNormal = normal;
+        this.mHigh = high;
+    }
+
+    /**
+     * @return the value for the category normal;
+     * @hide
+     */
+    public float getNormal() {
+        return mNormal;
+    }
+
+    /**
+     * @return the value for the category high;
+     * @hide
+     */
+    public float getHigh() {
+        return mHigh;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof FrameRateCategoryRate)) {
+            return false;
+        }
+        FrameRateCategoryRate that = (FrameRateCategoryRate) o;
+        return mNormal == that.mNormal && mHigh == that.mHigh;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 1;
+        result = 31 * result + Float.hashCode(mNormal);
+        result = 31 * result + Float.hashCode(mHigh);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "FrameRateCategoryRate {"
+                + "normal=" + mNormal
+                + ", high=" + mHigh
+                + '}';
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeFloat(mNormal);
+        dest.writeFloat(mHigh);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<FrameRateCategoryRate> CREATOR =
+            new Creator<>() {
+                @Override
+                public FrameRateCategoryRate createFromParcel(Parcel in) {
+                    return new FrameRateCategoryRate(in.readFloat(), in.readFloat());
+                }
+
+                @Override
+                public FrameRateCategoryRate[] newArray(int size) {
+                    return new FrameRateCategoryRate[size];
+                }
+            };
+}
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 94f415b..b8b22e2 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -87,6 +87,7 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -463,7 +464,7 @@
         /**
          * Called when new jank classifications are available.
          */
-        void onJankDataAvailable(JankData[] jankData);
+        void onJankDataAvailable(@NonNull List<JankData> jankData);
 
     }
 
@@ -1798,6 +1799,7 @@
         public int activeDisplayModeId;
         public float renderFrameRate;
         public boolean hasArrSupport;
+        public FrameRateCategoryRate frameRateCategoryRate;
 
         public int[] supportedColorModes;
         public int activeColorMode;
@@ -1816,6 +1818,7 @@
                     + ", activeDisplayModeId=" + activeDisplayModeId
                     + ", renderFrameRate=" + renderFrameRate
                     + ", hasArrSupport=" + hasArrSupport
+                    + ", frameRateCategoryRate=" + frameRateCategoryRate
                     + ", supportedColorModes=" + Arrays.toString(supportedColorModes)
                     + ", activeColorMode=" + activeColorMode
                     + ", hdrCapabilities=" + hdrCapabilities
@@ -1836,13 +1839,15 @@
                 && activeColorMode == that.activeColorMode
                 && Objects.equals(hdrCapabilities, that.hdrCapabilities)
                 && preferredBootDisplayMode == that.preferredBootDisplayMode
-                && hasArrSupport == that.hasArrSupport;
+                && hasArrSupport == that.hasArrSupport
+                && Objects.equals(frameRateCategoryRate, that.frameRateCategoryRate);
         }
 
         @Override
         public int hashCode() {
             return Objects.hash(Arrays.hashCode(supportedDisplayModes), activeDisplayModeId,
-                    renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport);
+                    renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport,
+                    frameRateCategoryRate);
         }
     }
 
@@ -2682,7 +2687,9 @@
      * Adds a callback to be informed about SF's jank classification for this surface.
      * @hide
      */
-    public OnJankDataListenerRegistration addJankDataListener(OnJankDataListener listener) {
+    @NonNull
+    public OnJankDataListenerRegistration addOnJankDataListener(
+            @NonNull OnJankDataListener listener) {
         return new OnJankDataListenerRegistration(this, listener);
     }
 
@@ -3455,15 +3462,15 @@
          * @return this This transaction for chaining
          * @hide
          */
-        public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, float top, float left,
-                float bottom, float right) {
+        public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, float left, float top,
+                float right, float bottom) {
             checkPreconditions(sc);
             if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
                 SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
                         "setCrop", this, sc, "crop={" + top + ", " + left + ", " +
                         bottom + ", " + right + "}");
             }
-            nativeSetCrop(mNativeObject, sc.mNativeObject, top, left, bottom, right);
+            nativeSetCrop(mNativeObject, sc.mNativeObject, left, top, right, bottom);
             return this;
         }
 
@@ -3919,6 +3926,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/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 9cad3e5..4df7649 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -40,6 +40,7 @@
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.Region;
 import android.graphics.RenderNode;
 import android.hardware.input.InputManager;
@@ -1666,7 +1667,7 @@
     }
 
     private final Rect mRTLastReportedPosition = new Rect();
-    private final Rect mRTLastSetCrop = new Rect();
+    private final RectF mRTLastSetCrop = new RectF();
 
     private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener {
         private final int mRtSurfaceWidth;
@@ -1711,16 +1712,18 @@
 
         @Override
         public void positionChanged(long frameNumber, int left, int top, int right, int bottom,
-                int clipLeft, int clipTop, int clipRight, int clipBottom) {
+                int clipLeft, int clipTop, int clipRight, int clipBottom,
+                int nodeWidth, int nodeHeight) {
             try {
                 if (DEBUG_POSITION) {
                     Log.d(TAG, String.format(
                             "%d updateSurfacePosition RenderWorker, frameNr = %d, "
                                     + "position = [%d, %d, %d, %d] clip = [%d, %d, %d, %d] "
-                                    + "surfaceSize = %dx%d",
+                                    + "surfaceSize = %dx%d renderNodeSize = %d%d",
                             System.identityHashCode(SurfaceView.this), frameNumber,
                             left, top, right, bottom, clipLeft, clipTop, clipRight, clipBottom,
-                            mRtSurfaceWidth, mRtSurfaceHeight));
+                            mRtSurfaceWidth, mRtSurfaceHeight,
+                            nodeWidth, nodeHeight));
                 }
                 synchronized (mSurfaceControlLock) {
                     if (mSurfaceControl == null) return;
@@ -1735,14 +1738,29 @@
                             mRTLastReportedPosition.top /*positionTop*/,
                             postScaleX, postScaleY);
 
-                    mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom);
+                    // The computed crop is in view-relative dimensions, however we need it to be
+                    // in buffer-relative dimensions. So scale the crop by the ratio between
+                    // the view's unscaled width/height (nodeWidth/Height), and the surface's
+                    // width/height
+                    // That is, if the Surface has a fixed size of 50x50, the SurfaceView is laid
+                    // out to a size of 100x100, and the SurfaceView is ultimately scaled to
+                    // 1000x1000, then we need to scale the crop by just the 2x from surface
+                    // domain to SV domain.
+                    final float surfaceToNodeScaleX = (float) mRtSurfaceWidth / (float) nodeWidth;
+                    final float surfaceToNodeScaleY = (float) mRtSurfaceHeight / (float) nodeHeight;
+                    mRTLastSetCrop.set(clipLeft * surfaceToNodeScaleX,
+                            clipTop * surfaceToNodeScaleY,
+                            clipRight * surfaceToNodeScaleX,
+                            clipBottom * surfaceToNodeScaleY);
+
                     if (DEBUG_POSITION) {
-                        Log.d(TAG, String.format("Setting layer crop = [%d, %d, %d, %d] "
+                        Log.d(TAG, String.format("Setting layer crop = [%f, %f, %f, %f] "
                                         + "from scale %f, %f", mRTLastSetCrop.left,
                                 mRTLastSetCrop.top, mRTLastSetCrop.right, mRTLastSetCrop.bottom,
-                                postScaleX, postScaleY));
+                                surfaceToNodeScaleX, surfaceToNodeScaleY));
                     }
-                    mPositionChangedTransaction.setCrop(mSurfaceControl, mRTLastSetCrop);
+                    mPositionChangedTransaction.setCrop(mSurfaceControl, mRTLastSetCrop.left,
+                            mRTLastSetCrop.top, mRTLastSetCrop.right, mRTLastSetCrop.bottom);
                     if (mRTLastSetCrop.isEmpty()) {
                         mPositionChangedTransaction.hide(mSurfaceControl);
                     } else {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d9092ee..e49eec6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -30,7 +30,9 @@
 import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+import static android.view.accessibility.Flags.FLAG_SUPPLEMENTAL_DESCRIPTION;
 import static android.view.accessibility.Flags.removeChildHoverCheckForTouchExploration;
+import static android.view.accessibility.Flags.supplementalDescription;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
@@ -50,6 +52,7 @@
 import static android.view.flags.Flags.toolkitFrameRateViewEnablingReadOnly;
 import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
 import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
+import static android.view.flags.Flags.toolkitViewgroupSetRequestedFrameRateApi;
 import static android.view.flags.Flags.viewVelocityApi;
 import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
 import static android.view.inputmethod.Flags.initiationWithoutInputConnection;
@@ -2467,6 +2470,8 @@
             toolkitFrameRateVelocityMappingReadOnly();
     private static boolean sToolkitFrameRateAnimationBugfix25q1FlagValue =
             toolkitFrameRateAnimationBugfix25q1();
+    private static boolean sToolkitViewGroupFrameRateApiFlagValue =
+            toolkitViewgroupSetRequestedFrameRateApi();
 
     // Used to set frame rate compatibility.
     @Surface.FrameRateCompatibility int mFrameRateCompatibility =
@@ -3805,6 +3810,8 @@
      *     1                            PFLAG4_HAS_DRAWN
      *    1                             PFLAG4_HAS_MOVED
      *   1                              PFLAG4_HAS_VIEW_PROPERTY_INVALIDATION
+     *  1                               PFLAG4_FORCED_OVERRIDE_FRAME_RATE
+     * 1                                PFLAG4_SELF_REQUESTED_FRAME_RATE
      * |-------|-------|-------|-------|
      */
 
@@ -3955,6 +3962,17 @@
      */
     private static final int PFLAG4_HAS_VIEW_PROPERTY_INVALIDATION = 0x20000000;
 
+    /**
+     * When set, this indicates whether the frame rate of the children should be
+     * forcibly overridden, even if it has been explicitly configured by a user request.
+     */
+    private static final int PFLAG4_FORCED_OVERRIDE_FRAME_RATE = 0x40000000;
+
+    /**
+     * When set, this indicates that the frame rate is configured based on a user request.
+     */
+    private static final int PFLAG4_SELF_REQUESTED_FRAME_RATE = 0x80000000;
+
     /* End of masks for mPrivateFlags4 */
 
     /** @hide */
@@ -4864,6 +4882,11 @@
     private CharSequence mContentDescription;
 
     /**
+     * Brief supplemental information for view and is primarily used for accessibility support.
+     */
+    private CharSequence mSupplementalDescription;
+
+    /**
      * If this view represents a distinct part of the window, it can have a title that labels the
      * area.
      */
@@ -6534,6 +6557,13 @@
                 case R.styleable.View_contentSensitivity:
                     setContentSensitivity(a.getInt(attr, CONTENT_SENSITIVITY_AUTO));
                     break;
+                default: {
+                    if (supplementalDescription()) {
+                        if (attr == com.android.internal.R.styleable.View_supplementalDescription) {
+                            setSupplementalDescription(a.getString(attr));
+                        }
+                    }
+                }
             }
         }
 
@@ -11806,6 +11836,39 @@
     }
 
     /**
+     * Returns the {@link View}'s supplemental description.
+     * <p>
+     * A supplemental description provides
+     * brief supplemental information for this node, such as the purpose of the node when
+     * that purpose is not conveyed within its textual representation. For example, if a
+     * dropdown select has a purpose of setting font family, the supplemental description
+     * could be "font family". If this node has children, its supplemental description serves
+     * as additional information and is not intended to replace any existing information
+     * in the subtree. This is different from the {@link #getContentDescription()} in that
+     * this description is purely supplemental while a content description may be used
+     * to replace a description for a node or its subtree that an assistive technology
+     * would otherwise compute based on other properties of the node and its descendants.
+     *
+     * <p>
+     * <strong>Note:</strong> Do not override this method, as it will have no
+     * effect on the supplemental description presented to accessibility services.
+     * You must call {@link #setSupplementalDescription(CharSequence)} to modify the
+     * supplemental description.
+     *
+     * @return the supplemental description
+     * @see #setSupplementalDescription(CharSequence)
+     * @see #getContentDescription()
+     * @attr ref android.R.styleable#View_supplementalDescription
+     */
+    @FlaggedApi(FLAG_SUPPLEMENTAL_DESCRIPTION)
+    @ViewDebug.ExportedProperty(category = "accessibility")
+    @InspectableProperty
+    @Nullable
+    public CharSequence getSupplementalDescription() {
+        return mSupplementalDescription;
+    }
+
+    /**
      * Sets the {@link View}'s state description.
      * <p>
      * A state description briefly describes the states of the view and is primarily used
@@ -11892,6 +11955,53 @@
     }
 
     /**
+     * Sets the {@link View}'s supplemental description.
+     * <p>
+     * A supplemental description provides
+     * brief supplemental information for this node, such as the purpose of the node when
+     * that purpose is not conveyed within its textual representation. For example, if a
+     * dropdown select has a purpose of setting font family, the supplemental description
+     * could be "font family". If this node has children, its supplemental description serves
+     * as additional information and is not intended to replace any existing information
+     * in the subtree. This is different from the {@link #setContentDescription(CharSequence)}
+     * in that this description is purely supplemental while a content description may be used
+     * to replace a description for a node or its subtree that an assistive technology
+     * would otherwise compute based on other properties of the node and its descendants.
+     *
+     * <p>
+     * This should omit role or state. Role refers to the kind of user-interface element the View
+     * is, such as a Button or Checkbox. State refers to a frequently changing property of the View,
+     * such as an On/Off state of a button or the audio level of a volume slider.
+     *
+     * @param supplementalDescription The supplemental description.
+     * @see #getSupplementalDescription()
+     * @see #setContentDescription(CharSequence)
+     * @see #setStateDescription(CharSequence) for state changes.
+     * @attr ref android.R.styleable#View_supplementalDescription
+     */
+    @FlaggedApi(FLAG_SUPPLEMENTAL_DESCRIPTION)
+    @RemotableViewMethod
+    public void setSupplementalDescription(@Nullable CharSequence supplementalDescription) {
+        if (mSupplementalDescription == null) {
+            if (supplementalDescription == null) {
+                return;
+            }
+        } else if (mSupplementalDescription.equals(supplementalDescription)) {
+            return;
+        }
+        mSupplementalDescription = supplementalDescription;
+        final boolean nonEmptyDesc = supplementalDescription != null
+                && !supplementalDescription.isEmpty();
+        if (nonEmptyDesc && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+            setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+            notifySubtreeAccessibilityStateChangedIfNeeded();
+        } else {
+            notifyViewAccessibilityStateChangedIfNeeded(
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION);
+        }
+    }
+
+    /**
      * Sets the id of a view that screen readers are requested to visit after this view.
      *
      * <p>
@@ -34198,9 +34308,20 @@
      */
     @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
     public void setRequestedFrameRate(float frameRate) {
+        // Skip setting the frame rate if it's currently in forced override mode.
+        if (sToolkitViewGroupFrameRateApiFlagValue && getForcedOverrideFrameRateFlag()) {
+            return;
+        }
+
         if (sToolkitSetFrameRateReadOnlyFlagValue) {
             mPreferredFrameRate = frameRate;
         }
+
+        if (sToolkitViewGroupFrameRateApiFlagValue) {
+            // If frameRate is Float.NaN, it means it's set to the default value.
+            // We only want to make the flag true, when the value is not Float.nan
+            setSelfRequestedFrameRateFlag(!Float.isNaN(mPreferredFrameRate));
+        }
     }
 
     /**
@@ -34224,4 +34345,35 @@
         }
         return 0;
     }
+
+    void overrideFrameRate(float frameRate, boolean forceOverride) {
+        setForcedOverrideFrameRateFlag(forceOverride);
+        if (forceOverride || !getSelfRequestedFrameRateFlag()) {
+            mPreferredFrameRate = frameRate;
+        }
+    }
+
+    void setForcedOverrideFrameRateFlag(boolean forcedOverride) {
+        if (forcedOverride) {
+            mPrivateFlags4 |= PFLAG4_FORCED_OVERRIDE_FRAME_RATE;
+        } else {
+            mPrivateFlags4 &= ~PFLAG4_FORCED_OVERRIDE_FRAME_RATE;
+        }
+    }
+
+    boolean getForcedOverrideFrameRateFlag() {
+        return (mPrivateFlags4 & PFLAG4_FORCED_OVERRIDE_FRAME_RATE) != 0;
+    }
+
+    void setSelfRequestedFrameRateFlag(boolean forcedOverride) {
+        if (forcedOverride) {
+            mPrivateFlags4 |= PFLAG4_SELF_REQUESTED_FRAME_RATE;
+        } else {
+            mPrivateFlags4 &= ~PFLAG4_SELF_REQUESTED_FRAME_RATE;
+        }
+    }
+
+    boolean getSelfRequestedFrameRateFlag() {
+        return (mPrivateFlags4 & PFLAG4_SELF_REQUESTED_FRAME_RATE) != 0;
+    }
 }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 2237417..4a9916c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -18,9 +18,12 @@
 
 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+import static android.view.flags.Flags.FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API;
+import static android.view.flags.Flags.toolkitViewgroupSetRequestedFrameRateApi;
 
 import android.animation.LayoutTransition;
 import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
 import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -448,6 +451,14 @@
     private static final int FLAG_SHOW_CONTEXT_MENU_WITH_COORDS = 0x20000000;
 
     /**
+     * When set, this indicates that the frame rate is passed down from the parent.
+     */
+    private static final int FLAG_PROPAGATED_FRAME_RATE = 0x40000000;
+
+    private static boolean sToolkitViewGroupFrameRateApiFlagValue =
+            toolkitViewgroupSetRequestedFrameRateApi();
+
+    /**
      * Indicates which types of drawing caches are to be kept in memory.
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
@@ -5361,6 +5372,12 @@
         }
 
         touchAccessibilityNodeProviderIfNeeded(child);
+
+        // If a propagated value exists, pass it to the child.
+        if (sToolkitViewGroupFrameRateApiFlagValue && !Float.isNaN(getRequestedFrameRate())
+                && (mGroupFlags & FLAG_PROPAGATED_FRAME_RATE) != 0) {
+            child.overrideFrameRate(getRequestedFrameRate(), getForcedOverrideFrameRateFlag());
+        }
     }
 
     /**
@@ -9465,4 +9482,75 @@
         }
         return null;
     }
+
+    /**
+     * You can set the preferred frame rate for a ViewGroup using a positive number
+     * or by specifying the preferred frame rate category using constants, including
+     * REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
+     * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, REQUESTED_FRAME_RATE_CATEGORY_HIGH.
+     * Keep in mind that the preferred frame rate affects the frame rate for the next frame,
+     * so use this method carefully. It's important to note that the preference is valid as
+     * long as the ViewGroup is invalidated. Please also be aware that the requested frame rate
+     * will not propagate to child views.
+     *
+     * @param frameRate the preferred frame rate of the ViewGroup.
+     */
+    @Override
+    @FlaggedApi(FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API)
+    public void setRequestedFrameRate(float frameRate) {
+        if (sToolkitViewGroupFrameRateApiFlagValue) {
+            if (getForcedOverrideFrameRateFlag()) {
+                return;
+            }
+            super.setRequestedFrameRate(frameRate);
+            // If frameRate is Float.NaN, it means it's set to the default value.
+            // We only want to make the flag true, when the value is not Float.nan
+            setSelfRequestedFrameRateFlag(!Float.isNaN(getRequestedFrameRate()));
+            mGroupFlags &= ~FLAG_PROPAGATED_FRAME_RATE;
+        }
+    }
+
+    /**
+     * You can set the preferred frame rate for a ViewGroup and its children using a positive number
+     * or by specifying the preferred frame rate category using constants, including
+     * REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
+     * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, REQUESTED_FRAME_RATE_CATEGORY_HIGH.
+     * Keep in mind that the preferred frame rate affects the frame rate for the next frame,
+     * so use this method carefully. It's important to note that the preference is valid as
+     * long as the ViewGroup or any of its children is invalidated.
+     * To undo the frame rate propagation, call the API with REQUESTED_FRAME_RATE_CATEGORY_DEFAULT.
+     *
+     * @param frameRate the preferred frame rate of the ViewGroup.
+     * @param forceOverride indicate whether it should override the frame rate of
+     *        all the children with the given frame rate.
+     */
+    @FlaggedApi(FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API)
+    public void propagateRequestedFrameRate(float frameRate, boolean forceOverride) {
+        if (sToolkitViewGroupFrameRateApiFlagValue) {
+            // Skip setting the frame rate if it's currently in forced override mode.
+            if (getForcedOverrideFrameRateFlag()) {
+                return;
+            }
+
+            // frame rate could be set previously with setRequestedFrameRate
+            // or propagateRequestedFrameRate
+            setSelfRequestedFrameRateFlag(false);
+            overrideFrameRate(frameRate, forceOverride);
+            setSelfRequestedFrameRateFlag(true);
+        }
+    }
+
+    @Override
+    void overrideFrameRate(float frameRate, boolean forceOverride) {
+        // if it's in forceOverrid mode or has no self requested frame rate,
+        // it will override the frame rate.
+        if (forceOverride || !getSelfRequestedFrameRateFlag()) {
+            super.overrideFrameRate(frameRate, forceOverride);
+            mGroupFlags |= FLAG_PROPAGATED_FRAME_RATE;
+
+            for (int i = 0; i < getChildCount(); i++) {
+                getChildAt(i).overrideFrameRate(frameRate, forceOverride);
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d57a880..3ce6870 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1132,6 +1132,8 @@
     // time for evaluating the interval between current time and
     // the time when frame rate was set previously.
     private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100;
+    // timeout for surface replaced.
+    private static final int FRAME_RATE_SURFACE_REPLACED_TIME = 3000;
 
     /*
      * The variables below are used to update frame rate category
@@ -3831,6 +3833,9 @@
                 if (surfaceReplaced) {
                     mSurfaceReplaced = true;
                     mSurfaceSequenceId++;
+                    mHandler.removeMessages(MSG_SURFACE_REPLACED_TIMEOUT);
+                    mHandler.sendEmptyMessageDelayed(MSG_SURFACE_REPLACED_TIMEOUT,
+                            FRAME_RATE_SURFACE_REPLACED_TIME);
                 }
                 if (alwaysConsumeSystemBarsChanged) {
                     mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
@@ -6544,7 +6549,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 +6558,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) {
@@ -6694,6 +6701,7 @@
     private static final int MSG_CHECK_INVALIDATION_IDLE = 40;
     private static final int MSG_REFRESH_POINTER_ICON = 41;
     private static final int MSG_FRAME_RATE_SETTING = 42;
+    private static final int MSG_SURFACE_REPLACED_TIMEOUT = 43;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -6765,6 +6773,8 @@
                     return "MSG_TOUCH_BOOST_TIMEOUT";
                 case MSG_FRAME_RATE_SETTING:
                     return "MSG_FRAME_RATE_SETTING";
+                case MSG_SURFACE_REPLACED_TIMEOUT:
+                    return "MSG_SURFACE_REPLACED_TIMEOUT";
             }
             return super.getMessageName(message);
         }
@@ -7036,6 +7046,9 @@
                      */
                     mIsFrameRateBoosting = false;
                     mIsTouchBoosting = false;
+                    if (!mDrawnThisFrame) {
+                        setPreferredFrameRateCategory(FRAME_RATE_CATEGORY_NO_PREFERENCE);
+                    }
                     break;
                 case MSG_REFRESH_POINTER_ICON:
                     if (mPointerIconEvent == null) {
@@ -7047,6 +7060,9 @@
                     mPreferredFrameRate = 0;
                     mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
                     break;
+                case MSG_SURFACE_REPLACED_TIMEOUT:
+                    mSurfaceReplaced = false;
+                    break;
             }
         }
     }
@@ -9323,7 +9339,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;
@@ -13390,6 +13406,7 @@
     private void removeVrrMessages() {
         mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
         mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+        mHandler.removeMessages(MSG_SURFACE_REPLACED_TIMEOUT);
         if (mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
             mInvalidationIdleMessagePosted = false;
             mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 4e9d054..c690787 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -810,6 +810,20 @@
     @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
     public static final int CONTENT_CHANGE_TYPE_EXPANDED = 1 << 14;
 
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * The source node changed its supplemental description, which is returned by
+     * {@link AccessibilityNodeInfo#getSupplementalDescription()}.
+     * The view changing its supplemental description should call
+     * {@link AccessibilityNodeInfo#setSupplementalDescription(CharSequence)} and
+     * then send this event.
+     *
+     * @see AccessibilityNodeInfo#getSupplementalDescription()
+     * @see AccessibilityNodeInfo#setSupplementalDescription(CharSequence)
+     */
+    @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
+    public static final int CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION = 1 << 15;
+
     // Speech state change types.
 
     /** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
@@ -942,6 +956,7 @@
                 CONTENT_CHANGE_TYPE_CONTENT_INVALID,
                 CONTENT_CHANGE_TYPE_ERROR,
                 CONTENT_CHANGE_TYPE_ENABLED,
+                CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION,
             })
     public @interface ContentChangeTypes {}
 
@@ -1222,7 +1237,14 @@
                 return "CONTENT_CHANGE_TYPE_CONTENT_INVALID";
             case CONTENT_CHANGE_TYPE_ERROR: return "CONTENT_CHANGE_TYPE_ERROR";
             case CONTENT_CHANGE_TYPE_ENABLED: return "CONTENT_CHANGE_TYPE_ENABLED";
-            default: return Integer.toHexString(type);
+            default: {
+                if (Flags.supplementalDescription()) {
+                    if (type == CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION) {
+                        return "CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION";
+                    }
+                }
+                return Integer.toHexString(type);
+            }
         }
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index d3e7faa..5e5f33e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -856,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.
      *
@@ -997,6 +997,8 @@
 
     private static final int BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING = 1 << 26;
 
+    private static final int BOOLEAN_PROPERTY_FIELD_REQUIRED = 1 << 27;
+
     /**
      * Bits that provide the id of a virtual descendant of a view.
      */
@@ -1085,6 +1087,7 @@
     private CharSequence mPaneTitle;
     private CharSequence mStateDescription;
     private CharSequence mContentDescription;
+    private CharSequence mSupplementalDescription;
     private CharSequence mTooltipText;
     private String mViewIdResourceName;
     private String mUniqueId;
@@ -2543,6 +2546,32 @@
     }
 
     /**
+     * Gets whether a node representing a form field requires input or selection.
+     *
+     * @return {@code true} if {@code this} node represents a form field that requires input or
+     *     selection, {@code false} otherwise.
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_IS_REQUIRED_API)
+    public boolean isFieldRequired() {
+        return getBooleanProperty(BOOLEAN_PROPERTY_FIELD_REQUIRED);
+    }
+
+    /**
+     * Sets whether {@code this} node represents a form field that requires input or selection.
+     *
+     * <p><strong>Note:</strong> Cannot be called from an AccessibilityService. This class is made
+     * immutable before being delivered to an AccessibilityService.
+     *
+     * @param required {@code true} if input or selection of this node should be required, {@code
+     *     false} otherwise.
+     * @throws IllegalStateException If called from an AccessibilityService
+     */
+    @FlaggedApi(Flags.FLAG_A11Y_IS_REQUIRED_API)
+    public void setFieldRequired(boolean required) {
+        setBooleanProperty(BOOLEAN_PROPERTY_FIELD_REQUIRED, required);
+    }
+
+    /**
      * Gets whether this node is focusable.
      *
      * <p>In the View system, this typically maps to {@link View#isFocusable()}.
@@ -3686,6 +3715,27 @@
         return mContentDescription;
     }
 
+    /**
+     * Gets the supplemental description of this node. A supplemental description provides
+     * brief supplemental information for this node, such as the purpose of the node when
+     * that purpose is not conveyed within its textual representation. For example, if a
+     * dropdown select has a purpose of setting font family, the supplemental description
+     * could be "font family". If this node has children, its supplemental description serves
+     * as additional information and is not intended to replace any existing information
+     * in the subtree. This is different from the {@link #getContentDescription()} in that
+     * this description is purely supplemental while a content description may be used
+     * to replace a description for a node or its subtree that an assistive technology
+     * would otherwise compute based on other properties of the node and its descendants.
+     *
+     * @return The supplemental description.
+     * @see #setSupplementalDescription(CharSequence)
+     * @see #getContentDescription()
+     */
+    @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
+    @Nullable
+    public CharSequence getSupplementalDescription() {
+        return mSupplementalDescription;
+    }
 
     /**
      * Sets the state description of this node.
@@ -3724,6 +3774,35 @@
     }
 
     /**
+     * Sets the supplemental description of this node. A supplemental description provides
+     * brief supplemental information for this node, such as the purpose of the node when
+     * that purpose is not conveyed within its textual representation. For example, if a
+     * dropdown select has a purpose of setting font family, the supplemental description
+     * could be "font family". If this node has children, its supplemental description serves
+     * as additional information and is not intended to replace any existing information
+     * in the subtree. This is different from the {@link #setContentDescription(CharSequence)}
+     * in that this description is purely supplemental while a content description may be used
+     * to replace a description for a node or its subtree that an assistive technology
+     * would otherwise compute based on other properties of the node and its descendants.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     *
+     * @param supplementalDescription The supplemental description.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     * @see #getSupplementalDescription()
+     * @see #setContentDescription(CharSequence)
+     */
+    @FlaggedApi(Flags.FLAG_SUPPLEMENTAL_DESCRIPTION)
+    public void setSupplementalDescription(@Nullable CharSequence supplementalDescription) {
+        enforceNotSealed();
+        mSupplementalDescription = (supplementalDescription == null) ? null
+                : supplementalDescription.subSequence(0, supplementalDescription.length());
+    }
+
+    /**
      * Gets the tooltip text of this node.
      *
      * @return The tooltip text.
@@ -4657,6 +4736,10 @@
             nonDefaultFields |= bitAt(fieldIndex);
         }
         fieldIndex++;
+        if (!Objects.equals(mSupplementalDescription, DEFAULT.mSupplementalDescription)) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
+        fieldIndex++;
         if (!Objects.equals(mPaneTitle, DEFAULT.mPaneTitle)) {
             nonDefaultFields |= bitAt(fieldIndex);
         }
@@ -4843,6 +4926,9 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
             parcel.writeCharSequence(mContentDescription);
         }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            parcel.writeCharSequence(mSupplementalDescription);
+        }
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle);
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText);
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mContainerTitle);
@@ -4950,6 +5036,7 @@
         mError = other.mError;
         mStateDescription = other.mStateDescription;
         mContentDescription = other.mContentDescription;
+        mSupplementalDescription = other.mSupplementalDescription;
         mPaneTitle = other.mPaneTitle;
         mTooltipText = other.mTooltipText;
         mContainerTitle = other.mContainerTitle;
@@ -5119,6 +5206,9 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
             mContentDescription = parcel.readCharSequence();
         }
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            mSupplementalDescription = parcel.readCharSequence();
+        }
         if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mContainerTitle = parcel.readCharSequence();
@@ -5483,6 +5573,9 @@
         builder.append("; maxTextLength: ").append(mMaxTextLength);
         builder.append("; stateDescription: ").append(mStateDescription);
         builder.append("; contentDescription: ").append(mContentDescription);
+        if (Flags.supplementalDescription()) {
+            builder.append("; supplementalDescription: ").append(mSupplementalDescription);
+        }
         builder.append("; tooltipText: ").append(mTooltipText);
         builder.append("; containerTitle: ").append(mContainerTitle);
         builder.append("; viewIdResName: ").append(mViewIdResourceName);
@@ -5491,6 +5584,9 @@
 
         builder.append("; checkable: ").append(isCheckable());
         builder.append("; checked: ").append(isChecked());
+        if (Flags.a11yIsRequiredApi()) {
+            builder.append("; required: ").append(isFieldRequired());
+        }
         builder.append("; focusable: ").append(isFocusable());
         builder.append("; focused: ").append(isFocused());
         builder.append("; selected: ").append(isSelected());
@@ -6417,6 +6513,19 @@
         /** Range type: percent with values from zero to one hundred. */
         public static final int RANGE_TYPE_PERCENT = 2;
 
+        /**
+         * Range type: indeterminate.
+         *
+         * A {@link RangeInfo} type used to represent a node which may typically expose range
+         * information but is presently in an indeterminate state, such as a {@link
+         * android.widget.ProgressBar} representing a loading operation of unknown duration.
+         * When using this type, the {@code min}, {@code max}, and {@code current} values used to
+         * construct an instance may be ignored. It is recommended to use {@code Float.NaN} for
+         * these values.
+         */
+        @FlaggedApi(Flags.FLAG_INDETERMINATE_RANGE_INFO)
+        public static final int RANGE_TYPE_INDETERMINATE = 3;
+
         private int mType;
         private float mMin;
         private float mMax;
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 820a1fb..c07da41 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -40,6 +40,13 @@
 }
 
 flag {
+    name: "a11y_character_in_window_api"
+    namespace: "accessibility"
+    description: "Enables new extra data key for an AccessibilityService to request character bounds in unmagnified window coordinates."
+    bug: "375429616"
+}
+
+flag {
     namespace: "accessibility"
     name: "allow_shortcut_chooser_on_lockscreen"
     description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
@@ -215,6 +222,13 @@
 }
 
 flag {
+    name: "supplemental_description"
+    namespace: "accessibility"
+    description: "Feature flag for supplemental description api"
+    bug: "375266174"
+}
+
+flag {
     name: "support_multiple_labeledby"
     namespace: "accessibility"
     description: "Feature flag for supporting multiple labels in AccessibilityNodeInfo labeledby api"
@@ -244,3 +258,10 @@
         purpose: PURPOSE_BUGFIX
     }
  }
+
+ flag {
+    name: "indeterminate_range_info"
+    namespace: "accessibility"
+    description: "Creates a way to create an INDETERMINATE RangeInfo"
+    bug: "376108874"
+ }
diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig
index c31df73..1c7570e 100644
--- a/core/java/android/view/flags/refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/refresh_rate_flags.aconfig
@@ -122,6 +122,14 @@
 }
 
 flag {
+    name: "toolkit_viewgroup_set_requested_frame_rate_api"
+    namespace: "toolkit"
+    description: "Feature flag to introduce new frame rate setting APIs on ViewGroup"
+    bug: "335874198"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "toolkit_frame_rate_touch_boost_25q1"
     namespace: "toolkit"
     description: "Feature flag to not suppress touch boost for specific windowTypes in VRR V QPR2"
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/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl
index aeb746c..39092fe 100644
--- a/core/java/android/webkit/IWebViewUpdateService.aidl
+++ b/core/java/android/webkit/IWebViewUpdateService.aidl
@@ -72,16 +72,6 @@
     PackageInfo getCurrentWebViewPackage();
 
     /**
-     * Used by Settings to determine whether multiprocess is enabled.
-     */
-    boolean isMultiProcessEnabled();
-
-    /**
-     * Used by Settings to enable/disable multiprocess.
-     */
-    void enableMultiProcess(boolean enable);
-
-    /**
      * Used by Settings to get the default WebView package.
      */
     WebViewProviderInfo getDefaultWebViewPackage();
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 4c5802c..778a51e 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -28,7 +28,6 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.RecordingCanvas;
-import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.SparseArray;
@@ -217,19 +216,7 @@
      * Returns whether WebView should run in multiprocess mode.
      */
     public boolean isMultiProcessEnabled() {
-        if (Flags.updateServiceV2()) {
-            return true;
-        } else if (Flags.updateServiceIpcWrapper()) {
-            // We don't want to support this method in the new wrapper because updateServiceV2 is
-            // intended to ship in the same release (or sooner). It's only possible to disable it
-            // with an obscure adb command, so just return true here too.
-            return true;
-        }
-        try {
-            return WebViewFactory.getUpdateService().isMultiProcessEnabled();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return true;
     }
 
     /**
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index e10a398..de303f8 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -16,8 +16,6 @@
 
 package android.webkit;
 
-import static android.webkit.Flags.updateServiceV2;
-
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.UptimeMillisLong;
@@ -490,7 +488,7 @@
                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
             }
 
-            if (updateServiceV2() && !isInstalledPackage(newPackageInfo)) {
+            if (!isInstalledPackage(newPackageInfo)) {
                 throw new MissingWebViewPackageException(
                         TextUtils.formatSimple(
                                 "Current WebView Package (%s) is not installed for the current "
@@ -498,7 +496,7 @@
                                 newPackageInfo.packageName));
             }
 
-            if (updateServiceV2() && !isEnabledPackage(newPackageInfo)) {
+            if (!isEnabledPackage(newPackageInfo)) {
                 throw new MissingWebViewPackageException(
                         TextUtils.formatSimple(
                                 "Current WebView Package (%s) is not enabled for the current user",
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/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 668cd01..10d16af 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -16,8 +16,6 @@
 
 package android.webkit;
 
-import static android.webkit.Flags.updateServiceV2;
-
 import android.content.pm.PackageInfo;
 import android.os.Build;
 import android.os.ChildZygoteProcess;
@@ -52,13 +50,6 @@
     @GuardedBy("sLock")
     private static PackageInfo sPackage;
 
-    /**
-     * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote will
-     * not be started. Should be removed entirely after we remove the updateServiceV2 flag.
-     */
-    @GuardedBy("sLock")
-    private static boolean sMultiprocessEnabled = false;
-
     public static ZygoteProcess getProcess() {
         synchronized (sLock) {
             if (sZygote != null) return sZygote;
@@ -76,40 +67,13 @@
 
     public static boolean isMultiprocessEnabled() {
         synchronized (sLock) {
-            if (updateServiceV2()) {
-                return sPackage != null;
-            } else {
-                return sMultiprocessEnabled && sPackage != null;
-            }
-        }
-    }
-
-    public static void setMultiprocessEnabled(boolean enabled) {
-        if (updateServiceV2()) {
-            throw new IllegalStateException(
-                    "setMultiprocessEnabled shouldn't be called if update_service_v2 flag is set.");
-        }
-        synchronized (sLock) {
-            sMultiprocessEnabled = enabled;
-
-            // When multi-process is disabled, kill the zygote. When it is enabled,
-            // the zygote will be started when it is first needed in getProcess().
-            if (!enabled) {
-                stopZygoteLocked();
-            }
+            return sPackage != null;
         }
     }
 
     static void onWebViewProviderChanged(PackageInfo packageInfo) {
         synchronized (sLock) {
             sPackage = packageInfo;
-
-            // If multi-process is not enabled, then do not start the zygote service.
-            // Only check sMultiprocessEnabled if updateServiceV2 is not enabled.
-            if (!updateServiceV2() && !sMultiprocessEnabled) {
-                return;
-            }
-
             stopZygoteLocked();
         }
     }
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 255bd67..65cd190 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -45,6 +45,7 @@
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 
+import java.time.DateTimeException;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.ZoneId;
@@ -291,11 +292,26 @@
     }
 
     private void createTime(String timeZone) {
-        if (timeZone != null) {
-            mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
+        TimeZone tz = null;
+        if (timeZone == null) {
+            tz = TimeZone.getDefault();
+            // Note that mTimeZone should always be null if timeZone is.
         } else {
-            mTime = Calendar.getInstance();
+            tz = TimeZone.getTimeZone(timeZone);
+            try {
+                // Try converting this TZ to a zoneId to make sure it's valid. This
+                // performs a different set of checks than TimeZone.getTimeZone so
+                // we can avoid exceptions later when we do need this conversion.
+                tz.toZoneId();
+            } catch (DateTimeException ex) {
+                // If we're here, the user supplied timezone is invalid, so reset
+                // mTimeZone to something sane.
+                tz = TimeZone.getDefault();
+                mTimeZone = tz.getID();
+            }
         }
+
+        mTime = Calendar.getInstance(tz);
     }
 
     /**
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 6cefc4d..4b7bacb 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -87,6 +87,13 @@
      */
     public static final String KEY_GESTURE_FINISHED = "GestureFinished";
 
+    /**
+     * Touch gestured has transferred to embedded window, Shell should pilfer pointers so the
+     * embedded won't receive motion events.
+     * @hide
+     */
+    public static final String KEY_TOUCH_GESTURE_TRANSFERRED = "TouchGestureTransferred";
+
 
     /**
      * Defines the type of back destinations a back even can lead to. This is used to define the
@@ -119,7 +126,7 @@
     @NonNull
     private final Rect mTouchableRegion;
 
-    private final boolean mAppProgressGenerationAllowed;
+    private boolean mAppProgressGenerationAllowed;
     private final int mFocusedTaskId;
 
     /**
@@ -253,6 +260,14 @@
     }
 
     /**
+     * Force disable app to intercept back progress event.
+     * @hide
+     */
+    public void disableAppProgressGenerationAllowed() {
+        mAppProgressGenerationAllowed = false;
+    }
+
+    /**
      * Callback to be called when the back preview is finished in order to notify the server that
      * it can clean up the resources created for the animation.
      * @hide
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 22eec29..dae87dd 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -70,7 +70,12 @@
     ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
     ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false),
     ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
-            Flags::enableWindowingTransitionHandlersObservers, false);
+            Flags::enableWindowingTransitionHandlersObservers, false),
+    ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS(
+            Flags::enableDesktopAppLaunchAlttabTransitions, false),
+    ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS(
+            Flags::enableDesktopAppLaunchTransitions, false),
+    ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false);
 
     private static final String TAG = "DesktopModeFlagsUtil";
     // Function called to obtain aconfig flag value.
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 34abf31..3fe63ab 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -1867,27 +1867,33 @@
             switch (type) {
                 case HIERARCHY_OP_TYPE_REPARENT: return "reparent";
                 case HIERARCHY_OP_TYPE_REORDER: return "reorder";
-                case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: return "ChildrenTasksReparent";
-                case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: return "SetLaunchRoot";
-                case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "SetAdjacentRoot";
-                case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "LaunchTask";
-                case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "SetAdjacentFlagRoot";
+                case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: return "childrenTasksReparent";
+                case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: return "setLaunchRoot";
+                case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "setAdjacentRoot";
+                case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "launchTask";
+                case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "setAdjacentFlagRoot";
                 case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT:
-                    return "SetDisableLaunchAdjacent";
-                case HIERARCHY_OP_TYPE_PENDING_INTENT: return "PendingIntent";
-                case HIERARCHY_OP_TYPE_START_SHORTCUT: return "StartShortcut";
+                    return "setDisableLaunchAdjacent";
+                case HIERARCHY_OP_TYPE_PENDING_INTENT: return "pendingIntent";
+                case HIERARCHY_OP_TYPE_START_SHORTCUT: return "startShortcut";
+                case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: return "restoreTransientOrder";
                 case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: return "addInsetsFrameProvider";
                 case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER:
                     return "removeInsetsFrameProvider";
                 case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: return "setAlwaysOnTop";
-                case HIERARCHY_OP_TYPE_REMOVE_TASK: return "RemoveTask";
+                case HIERARCHY_OP_TYPE_REMOVE_TASK: return "removeTask";
                 case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: return "finishActivity";
-                case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "ClearAdjacentRoot";
+                case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "clearAdjacentRoot";
                 case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
                     return "setReparentLeafTaskIfRelaunch";
                 case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION:
                     return "addTaskFragmentOperation";
+                case HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK:
+                    return "movePipActivityToPinnedTask";
+                case HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE: return "setIsTrimmable";
+                case HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION: return "restoreBackNav";
                 case HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES: return "setExcludeInsetsTypes";
+                case HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE: return "setKeyguardState";
                 default: return "HOP(" + type + ")";
             }
         }
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index c4a9e57..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"
@@ -369,3 +376,10 @@
     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/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 57aacff..fd5de91 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -9,13 +9,6 @@
 }
 
 flag {
-    name: "bal_require_opt_in_same_uid"
-    namespace: "responsible_apis"
-    description: "Require the PendingIntent creator/sender to opt in if it is the same UID"
-    bug: "296478951"
-}
-
-flag {
     name: "bal_dont_bring_existing_background_task_stack_to_fg"
     namespace: "responsible_apis"
     description: "When starting a PendingIntent with ONLY creator privileges, don't bring the existing task stack to foreground"
@@ -71,6 +64,7 @@
     bug: "339720406"
 }
 
+# replaced by bal_strict_mode_ro
 flag {
     name: "bal_strict_mode"
     namespace: "responsible_apis"
@@ -79,6 +73,14 @@
 }
 
 flag {
+    name: "bal_strict_mode_ro"
+    namespace: "responsible_apis"
+    description: "Strict mode flag"
+    bug: "324089586"
+    is_fixed_read_only: true
+}
+
+flag {
     name: "bal_reduce_grace_period"
     namespace: "responsible_apis"
     description: "Changes to reduce or ideally remove the grace period exemption."
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 966e835..3a03508 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -340,3 +340,22 @@
     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
+}
+
+flag {
+  name: "disallow_app_progress_embedded_window"
+  namespace: "windowing_frontend"
+  description: "Pilfer pointers when app transfer input gesture to embedded window."
+  bug: "365504126"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index be3f10a..091975c 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -28,6 +28,7 @@
 import android.widget.Filter;
 import android.widget.Filterable;
 import android.widget.LinearLayout;
+import android.widget.RadioButton;
 import android.widget.TextView;
 
 import com.android.internal.R;
@@ -242,11 +243,7 @@
                 break;
             case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER:
                 TextView title;
-                LocaleStore.LocaleInfo info = (LocaleStore.LocaleInfo) getItem(position);
-                if (info == null) {
-                    throw new NullPointerException("Non header locale cannot be null.");
-                }
-                if (info.isAppCurrentLocale()) {
+                if (mHasSpecificAppPackageName) {
                     title = itemView.findViewById(R.id.language_picker_item);
                 } else {
                     title = itemView.findViewById(R.id.locale);
@@ -254,11 +251,22 @@
                 title.setText(R.string.system_locale_title);
                 break;
             case TYPE_CURRENT_LOCALE:
-                updateTextView(itemView,
-                        itemView.findViewById(R.id.language_picker_item), position);
+                updateTextView(
+                        itemView, itemView.findViewById(R.id.language_picker_item), position);
                 break;
             default:
-                updateTextView(itemView, itemView.findViewById(R.id.locale), position);
+                LocaleStore.LocaleInfo localeInfo = (LocaleStore.LocaleInfo) getItem(position);
+                if (localeInfo == null) {
+                    throw new NullPointerException("Non header locale cannot be null.");
+                }
+                if (mHasSpecificAppPackageName && localeInfo.isSuggested() && !mCountryMode) {
+                    updateTextView(
+                            itemView,
+                            itemView.findViewById(R.id.language_picker_item),
+                            position);
+                } else {
+                    updateTextView(itemView, itemView.findViewById(R.id.locale), position);
+                }
                 break;
         }
         return itemView;
@@ -280,20 +288,21 @@
                 }
                 break;
             case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER:
-                if (((LocaleStore.LocaleInfo) getItem(position)).isAppCurrentLocale()) {
-                    shouldReuseView = convertView instanceof LinearLayout
-                            && convertView.findViewById(R.id.language_picker_item) != null;
-                    if (!shouldReuseView) {
-                        updatedView = mInflater.inflate(
-                                R.layout.app_language_picker_current_locale_item,
-                                parent, false);
+                if (mHasSpecificAppPackageName) {
+                    updatedView = mInflater.inflate(
+                        R.layout.app_language_picker_locale_item, parent, false);
+                    RadioButton option = updatedView.findViewById(R.id.checkbox);
+                    if (((LocaleStore.LocaleInfo) getItem(position)).isAppCurrentLocale()) {
+                        option.setChecked(true);
+                    } else {
+                        option.setChecked(false);
                     }
                 } else {
                     shouldReuseView = convertView instanceof TextView
-                            && convertView.findViewById(R.id.locale) != null;
+                        && convertView.findViewById(R.id.locale) != null;
                     if (!shouldReuseView) {
-                        updatedView = mInflater.inflate(
-                                R.layout.language_picker_item, parent, false);
+                        updatedView =
+                            mInflater.inflate(R.layout.language_picker_item, parent, false);
                     }
                 }
                 break;
@@ -302,14 +311,30 @@
                         && convertView.findViewById(R.id.language_picker_item) != null;
                 if (!shouldReuseView) {
                     updatedView = mInflater.inflate(
-                            R.layout.app_language_picker_current_locale_item, parent, false);
+                            R.layout.app_language_picker_locale_item, parent, false);
+                    RadioButton option = updatedView.findViewById(R.id.checkbox);
+                    option.setChecked(true);
                 }
                 break;
             default:
-                shouldReuseView = convertView instanceof TextView
+                LocaleStore.LocaleInfo localeInfo = (LocaleStore.LocaleInfo) getItem(position);
+                if (mHasSpecificAppPackageName
+                        && localeInfo.isSuggested() && !mCountryMode) {
+                    shouldReuseView = convertView instanceof LinearLayout
+                        && convertView.findViewById(R.id.language_picker_item) != null;
+                    if ((!shouldReuseView)) {
+                        updatedView = mInflater.inflate(
+                            R.layout.app_language_picker_locale_item, parent, false);
+                        RadioButton option = updatedView.findViewById(R.id.checkbox);
+                        option.setChecked(false);
+                    }
+                } else {
+                    shouldReuseView = convertView instanceof TextView
                         && convertView.findViewById(R.id.locale) != null;
-                if (!shouldReuseView) {
-                    updatedView = mInflater.inflate(R.layout.language_picker_item, parent, false);
+                    if ((!shouldReuseView)) {
+                        updatedView = mInflater.inflate(
+                            R.layout.language_picker_item, parent, false);
+                    }
                 }
                 break;
         }
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 0af4bea..44c0bd0 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -54,6 +54,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -448,7 +449,7 @@
     }
 
     @Override
-    public void onJankDataAvailable(SurfaceControl.JankData[] jankData) {
+    public void onJankDataAvailable(List<SurfaceControl.JankData> jankData) {
         postCallback(() -> {
             try {
                 Trace.beginSection("FrameTracker#onJankDataAvailable");
@@ -832,7 +833,7 @@
         /** adds the jank listener to the given surface */
         public SurfaceControl.OnJankDataListenerRegistration addJankStatsListener(
                 SurfaceControl.OnJankDataListener listener, SurfaceControl surfaceControl) {
-            return surfaceControl.addJankDataListener(listener);
+            return surfaceControl.addOnJankDataListener(listener);
         }
     }
 
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/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index 032ac42..6b6b81f 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -421,6 +421,10 @@
     @NonNull
     private String[] mUsesStaticLibrariesSorted;
 
+    private Map<String, Boolean> mFeatureFlagState = new ArrayMap<>();
+
+    private int mIntentMatchingFlags;
+
     @NonNull
     public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath,
             @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp,
@@ -2819,6 +2823,7 @@
         queriesProviders = Collections.unmodifiableSet(queriesProviders);
         mimeGroups = Collections.unmodifiableSet(mimeGroups);
         mKnownActivityEmbeddingCerts = Collections.unmodifiableSet(mKnownActivityEmbeddingCerts);
+        mFeatureFlagState = Collections.unmodifiableMap(mFeatureFlagState);
     }
 
     @Override
@@ -3118,6 +3123,8 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        writeFeatureFlagState(dest);
+
         sForBoolean.parcel(this.supportsSmallScreens, dest, flags);
         sForBoolean.parcel(this.supportsNormalScreens, dest, flags);
         sForBoolean.parcel(this.supportsLargeScreens, dest, flags);
@@ -3265,6 +3272,28 @@
         dest.writeLong(this.mBooleans);
         dest.writeLong(this.mBooleans2);
         dest.writeBoolean(this.mAllowCrossUidActivitySwitchFromBelow);
+        dest.writeInt(this.mIntentMatchingFlags);
+    }
+
+    private void writeFeatureFlagState(@NonNull Parcel dest) {
+        // Use a string array to encode flag state. One string per flag in the form `<flag>=<value>`
+        // where value is 0 (disabled), 1 (enabled) or ? (unknown flag or value).
+        int featureFlagCount = this.mFeatureFlagState.size();
+        String[] featureFlagStateAsArray = new String[featureFlagCount];
+        var entryIterator = this.mFeatureFlagState.entrySet().iterator();
+        for (int i = 0; i < featureFlagCount; i++) {
+            var entry = entryIterator.next();
+            Boolean flagValue = entry.getValue();
+            if (flagValue == null) {
+                featureFlagStateAsArray[i] = entry.getKey() + "=?";
+            } else if (flagValue.booleanValue()) {
+                featureFlagStateAsArray[i] = entry.getKey() + "=1";
+            } else {
+                featureFlagStateAsArray[i] = entry.getKey() + "=0";
+            }
+
+        }
+        dest.writeStringArray(featureFlagStateAsArray);
     }
 
     public PackageImpl(Parcel in) {
@@ -3275,6 +3304,9 @@
         mCallback = callback;
         // We use the boot classloader for all classes that we load.
         final ClassLoader boot = Object.class.getClassLoader();
+
+        readFeatureFlagState(in);
+
         this.supportsSmallScreens = sForBoolean.unparcel(in);
         this.supportsNormalScreens = sForBoolean.unparcel(in);
         this.supportsLargeScreens = sForBoolean.unparcel(in);
@@ -3432,6 +3464,7 @@
         this.mBooleans = in.readLong();
         this.mBooleans2 = in.readLong();
         this.mAllowCrossUidActivitySwitchFromBelow = in.readBoolean();
+        this.mIntentMatchingFlags = in.readInt();
 
         assignDerivedFields();
         assignDerivedFields2();
@@ -3440,6 +3473,27 @@
         // to mutate this instance before it's finalized.
     }
 
+    private void readFeatureFlagState(@NonNull Parcel in) {
+        // See comment in writeFeatureFlagState() for encoding of flag state.
+        String[] featureFlagStateAsArray = in.createStringArray();
+        for (String s : featureFlagStateAsArray) {
+            int sepIndex = s.lastIndexOf('=');
+            if (sepIndex >= 0 && sepIndex == s.length() - 2) {
+                String flagPackageAndName = s.substring(0, sepIndex);
+                char c = s.charAt(sepIndex + 1);
+                Boolean flagValue = null;
+                if (c == '1') {
+                    flagValue = Boolean.TRUE;
+                } else if (c == '0') {
+                    flagValue = Boolean.FALSE;
+                } else if (c != '?') {
+                    continue;
+                }
+                this.mFeatureFlagState.put(flagPackageAndName, flagValue);
+            }
+        }
+    }
+
     @NonNull
     public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
         @Override
@@ -3649,6 +3703,17 @@
         return this;
     }
 
+    @Override
+    public ParsingPackage setIntentMatchingFlags(int intentMatchingFlags) {
+        mIntentMatchingFlags = intentMatchingFlags;
+        return this;
+    }
+
+    @Override
+    public int getIntentMatchingFlags() {
+        return mIntentMatchingFlags;
+    }
+
     // The following methods are explicitly not inside any interface. These are hidden under
     // PackageImpl which is only accessible to the system server. This is to prevent/discourage
     // usage of these fields outside of the utility classes.
@@ -3660,6 +3725,18 @@
         return mBaseAppDataDeviceProtectedDirForSystemUser;
     }
 
+    @Override
+    public PackageImpl addFeatureFlag(
+            @NonNull String flagPackageAndName,
+            @Nullable Boolean flagValue) {
+        mFeatureFlagState.put(flagPackageAndName, flagValue);
+        return this;
+    }
+
+    public Map<String, Boolean> getFeatureFlagState() {
+        return mFeatureFlagState;
+    }
+
     /**
      * Flags used for a internal bitset. These flags should never be persisted or exposed outside
      * of this class. It is expected that PackageCacher explicitly clears itself whenever the
diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
index 8faaf95..70b7953 100644
--- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
+++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
@@ -33,6 +33,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.modules.utils.TypedXmlPullParser;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -199,7 +200,7 @@
      * @return the current value of the given Aconfig flag, or null if there is no such flag
      */
     @Nullable
-    private Boolean getFlagValue(@NonNull String flagPackageAndName) {
+    public Boolean getFlagValue(@NonNull String flagPackageAndName) {
         Boolean value = mFlagValues.get(flagPackageAndName);
         if (DEBUG) {
             Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value);
@@ -209,10 +210,13 @@
 
     /**
      * Check if the element in {@code parser} should be skipped because of the feature flag.
+     * @param pkg The package being parsed
      * @param parser XML parser object currently parsing an element
      * @return true if the element is disabled because of its feature flag
      */
-    public boolean skipCurrentElement(@NonNull XmlResourceParser parser) {
+    public boolean skipCurrentElement(
+            @NonNull ParsingPackage pkg,
+            @NonNull XmlResourceParser parser) {
         if (!Flags.manifestFlagging()) {
             return false;
         }
@@ -227,18 +231,21 @@
             featureFlag = featureFlag.substring(1).strip();
         }
         final Boolean flagValue = getFlagValue(featureFlag);
+        boolean shouldSkip = false;
         if (flagValue == null) {
             Slog.w(LOG_TAG, "Skipping element " + parser.getName()
                     + " due to unknown feature flag " + featureFlag);
-            return true;
-        }
-        // Skip if flag==false && attr=="flag" OR flag==true && attr=="!flag" (negated)
-        if (flagValue == negated) {
+            shouldSkip = true;
+        } else if (flagValue == negated) {
+            // Skip if flag==false && attr=="flag" OR flag==true && attr=="!flag" (negated)
             Slog.i(LOG_TAG, "Skipping element " + parser.getName()
                     + " behind feature flag " + featureFlag + " = " + flagValue);
-            return true;
+            shouldSkip = true;
         }
-        return false;
+        if (android.content.pm.Flags.includeFeatureFlagsInPackageCacher()) {
+            pkg.addFeatureFlag(featureFlag, flagValue);
+        }
+        return shouldSkip;
     }
 
     /**
diff --git a/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java
index 8858f94..335dedd 100644
--- a/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java
@@ -61,7 +61,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
diff --git a/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java
index bb01581..5f48d16 100644
--- a/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java
+++ b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java
@@ -54,7 +54,7 @@
             return input.skip("install-constraints cannot be used by this package");
         }
 
-        ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser);
+        ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, pkg, res, parser);
         if (prefixes.isSuccess()) {
             if (validateFingerprintPrefixes(prefixes.getResult())) {
                 return input.success(pkg);
@@ -68,7 +68,7 @@
     }
 
     private static ParseResult<Set<String>> parseFingerprintPrefixes(
-            ParseInput input, Resources res, XmlResourceParser parser)
+            ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
             throws XmlPullParserException, IOException {
         Set<String> prefixes = new ArraySet<>();
         int type;
@@ -81,7 +81,7 @@
                 }
                 return input.success(prefixes);
             } else if (type == XmlPullParser.START_TAG) {
-                if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+                if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                     continue;
                 }
                 if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
index 55baa53..219e885 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java
@@ -109,7 +109,8 @@
                             R.styleable.AndroidManifestActivity_process,
                             R.styleable.AndroidManifestActivity_roundIcon,
                             R.styleable.AndroidManifestActivity_splitName,
-                            R.styleable.AndroidManifestActivity_attributionTags);
+                            R.styleable.AndroidManifestActivity_attributionTags,
+                            R.styleable.AndroidManifestActivity_intentMatchingFlags);
             if (result.isError()) {
                 return input.error(result);
             }
@@ -310,7 +311,8 @@
                     NOT_SET /*processAttr*/,
                     R.styleable.AndroidManifestActivityAlias_roundIcon,
                     NOT_SET /*splitNameAttr*/,
-                    R.styleable.AndroidManifestActivityAlias_attributionTags);
+                    R.styleable.AndroidManifestActivityAlias_attributionTags,
+                    R.styleable.AndroidManifestActivityAlias_intentMatchingFlags);
             if (result.isError()) {
                 return input.error(result);
             }
@@ -393,7 +395,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
index da48b23..39d7af6 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java
@@ -99,7 +99,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
@@ -200,7 +200,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java
index 291ed0c..54e67360 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java
@@ -45,4 +45,9 @@
 
     @Nullable
     String getSplitName();
+
+    /**
+     * Returns the intent matching flags.
+     */
+    int getIntentMatchingFlags();
 }
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java
index bb8f565..678e999 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java
@@ -24,7 +24,6 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 
@@ -34,7 +33,6 @@
  * @hide
  */
 @DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = false)
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public class ParsedMainComponentImpl extends ParsedComponentImpl implements ParsedMainComponent,
         Parcelable {
 
@@ -51,6 +49,30 @@
     @Nullable
     private String[] attributionTags;
 
+    private int mIntentMatchingFlags;
+
+    /**
+     * Opt-out of all intent filter matching rules. The value corresponds to the <code>none</code>
+     * value of {@link android.R.attr#intentMatchingFlags}
+     * @hide
+     */
+    public static final int INTENT_MATCHING_FLAGS_NONE = 1;
+
+    /**
+     * Opt-in to enforce intent filter matching. The value corresponds to the
+     * <code>enforceIntentFilter</code> value of {@link android.R.attr#intentMatchingFlags}
+     * @hide
+     */
+    public static final int INTENT_MATCHING_FLAGS_ENFORCE_INTENT_FILTER = 1 << 1;
+
+    /**
+     * Allows intent filters to match actions even when the action value is null. The value
+     * corresponds to the <code>allowNullAction</code> value of
+     * {@link android.R.attr#intentMatchingFlags}
+     * @hide
+     */
+    public static final int INTENT_MATCHING_FLAGS_ALLOW_NULL_ACTION = 1 << 2;
+
     public ParsedMainComponentImpl() {
     }
 
@@ -83,6 +105,20 @@
         return attributionTags == null ? EmptyArray.STRING : attributionTags;
     }
 
+    /**
+     * Sets the intent matching flags. This value is intended to be set from the "component" tags.
+     * @see android.R.styleable#AndroidManifestApplication_intentMatchingFlags
+     */
+    public ParsedMainComponent setIntentMatchingFlags(int intentMatchingFlags) {
+        mIntentMatchingFlags = intentMatchingFlags;
+        return this;
+    }
+
+    @Override
+    public int getIntentMatchingFlags() {
+        return this.mIntentMatchingFlags;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -98,6 +134,7 @@
         dest.writeInt(this.order);
         dest.writeString(this.splitName);
         dest.writeString8Array(this.attributionTags);
+        dest.writeInt(this.mIntentMatchingFlags);
     }
 
     protected ParsedMainComponentImpl(Parcel in) {
@@ -109,6 +146,7 @@
         this.order = in.readInt();
         this.splitName = in.readString();
         this.attributionTags = in.createString8Array();
+        this.mIntentMatchingFlags = in.readInt();
     }
 
     public static final Parcelable.Creator<ParsedMainComponentImpl> CREATOR =
@@ -139,6 +177,28 @@
     //@formatter:off
 
 
+    @android.annotation.IntDef(prefix = "INTENT_MATCHING_FLAGS_", value = {
+        INTENT_MATCHING_FLAGS_NONE,
+        INTENT_MATCHING_FLAGS_ENFORCE_INTENT_FILTER,
+        INTENT_MATCHING_FLAGS_ALLOW_NULL_ACTION
+    })
+    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface IntentMatchingFlags {}
+
+    @DataClass.Generated.Member
+    public static String intentMatchingFlagsToString(@IntentMatchingFlags int value) {
+        switch (value) {
+            case INTENT_MATCHING_FLAGS_NONE:
+                    return "INTENT_MATCHING_FLAGS_NONE";
+            case INTENT_MATCHING_FLAGS_ENFORCE_INTENT_FILTER:
+                    return "INTENT_MATCHING_FLAGS_ENFORCE_INTENT_FILTER";
+            case INTENT_MATCHING_FLAGS_ALLOW_NULL_ACTION:
+                    return "INTENT_MATCHING_FLAGS_ALLOW_NULL_ACTION";
+            default: return Integer.toHexString(value);
+        }
+    }
+
     @DataClass.Generated.Member
     public ParsedMainComponentImpl(
             @Nullable String processName,
@@ -147,7 +207,8 @@
             boolean exported,
             int order,
             @Nullable String splitName,
-            @Nullable String[] attributionTags) {
+            @Nullable String[] attributionTags,
+            int intentMatchingFlags) {
         this.processName = processName;
         this.directBootAware = directBootAware;
         this.enabled = enabled;
@@ -155,6 +216,7 @@
         this.order = order;
         this.splitName = splitName;
         this.attributionTags = attributionTags;
+        this.mIntentMatchingFlags = intentMatchingFlags;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -226,10 +288,10 @@
     }
 
     @DataClass.Generated(
-            time = 1701447884766L,
+            time = 1729613643190L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java",
-            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate  boolean directBootAware\nprivate  boolean enabled\nprivate  boolean exported\nprivate  int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final  android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedMainComponentImpl> CREATOR\npublic  com.android.internal.pm.pkg.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic  java.lang.String getClassName()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getAttributionTags()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedMainComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate  boolean directBootAware\nprivate  boolean enabled\nprivate  boolean exported\nprivate  int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\nprivate  int mIntentMatchingFlags\npublic static final  int INTENT_MATCHING_FLAGS_NONE\npublic static final  int INTENT_MATCHING_FLAGS_ENFORCE_INTENT_FILTER\npublic static final  int INTENT_MATCHING_FLAGS_ALLOW_NULL_ACTION\npublic static final  android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedMainComponentImpl> CREATOR\npublic  com.android.internal.pm.pkg.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic  java.lang.String getClassName()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getAttributionTags()\npublic  com.android.internal.pm.pkg.component.ParsedMainComponent setIntentMatchingFlags(int)\npublic @java.lang.Override int getIntentMatchingFlags()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedMainComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java
index 7e56180..4feb894 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.IntentFilter;
@@ -39,7 +40,8 @@
 import java.io.IOException;
 
 /** @hide */
-class ParsedMainComponentUtils {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class ParsedMainComponentUtils {
 
     private static final String TAG = ParsingUtils.TAG;
 
@@ -47,10 +49,11 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     static <Component extends ParsedMainComponentImpl> ParseResult<Component> parseMainComponent(
             Component component, String tag, String[] separateProcesses, ParsingPackage pkg,
-            TypedArray array, int flags, boolean useRoundIcon,  @Nullable String defaultSplitName,
+            TypedArray array, int flags, boolean useRoundIcon, @Nullable String defaultSplitName,
             @NonNull ParseInput input, int bannerAttr, int descriptionAttr, int directBootAwareAttr,
             int enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
-            int processAttr, int roundIconAttr, int splitNameAttr, int attributionTagsAttr) {
+            int processAttr, int roundIconAttr, int splitNameAttr, int attributionTagsAttr,
+            int intentMatchingFlagsAttr) {
         ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
                 array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
                 logoAttr, nameAttr, roundIconAttr);
@@ -107,6 +110,12 @@
             }
         }
 
+        if (android.security.Flags.enableIntentMatchingFlags()) {
+            int resolvedFlags = resolveIntentMatchingFlags(
+                        pkg.getIntentMatchingFlags(), array.getInt(intentMatchingFlagsAttr, 0));
+            component.setIntentMatchingFlags(resolvedFlags);
+        }
+
         return input.success(component);
     }
 
@@ -147,4 +156,21 @@
         return input.success(intentResult.getResult());
     }
 
+    /**
+     * Resolves intent matching flags from the application and a component, prioritizing the
+     * component's flags.
+     *
+     * @param applicationFlags The flag value from the "application" tag.
+     * @param componentFlags   The flag value from the "component" tags.
+     * @return The resolved intent matching flags.
+     */
+    @FlaggedApi(android.security.Flags.FLAG_ENABLE_INTENT_MATCHING_FLAGS)
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public static int resolveIntentMatchingFlags(int applicationFlags, int componentFlags) {
+        if (componentFlags == 0) {
+            return applicationFlags;
+        } else {
+            return componentFlags;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
index 6af2a29..5c39827 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java
@@ -79,7 +79,8 @@
                             R.styleable.AndroidManifestProvider_process,
                             R.styleable.AndroidManifestProvider_roundIcon,
                             R.styleable.AndroidManifestProvider_splitName,
-                            R.styleable.AndroidManifestProvider_attributionTags);
+                            R.styleable.AndroidManifestProvider_attributionTags,
+                            R.styleable.AndroidManifestProvider_intentMatchingFlags);
             if (result.isError()) {
                 return input.error(result);
             }
@@ -174,7 +175,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
index c68ea2d..c469a7a 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java
@@ -74,7 +74,8 @@
                     R.styleable.AndroidManifestService_process,
                     R.styleable.AndroidManifestService_roundIcon,
                     R.styleable.AndroidManifestService_splitName,
-                    R.styleable.AndroidManifestService_attributionTags
+                    R.styleable.AndroidManifestService_attributionTags,
+                    R.styleable.AndroidManifestService_intentMatchingFlags
             );
 
             if (result.isError()) {
@@ -138,7 +139,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(parser)) {
+            if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
index 5d185af..f4bceb8 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -123,6 +123,9 @@
 
     ParsingPackage addQueriesProvider(String authority);
 
+    /** Adds a feature flag (`android:featureFlag` attribute) encountered in the manifest. */
+    ParsingPackage addFeatureFlag(@NonNull String flagPackageAndName, @Nullable Boolean flagValue);
+
     /** Sets a process name -> {@link ParsedProcess} map coming from the <processes> tag. */
     ParsingPackage setProcesses(@NonNull Map<String, ParsedProcess> processes);
 
@@ -550,4 +553,15 @@
     boolean isNormalScreensSupported();
 
     boolean isSmallScreensSupported();
+
+    /**
+     * Sets the intent matching flags. This value is intended to be set from the "application" tag.
+     * @see android.R.styleable#AndroidManifestApplication_intentMatchingFlags
+     */
+    ParsingPackage setIntentMatchingFlags(int intentMatchingFlags);
+
+    /**
+     * Returns the intent matching flags.
+     */
+    int getIntentMatchingFlags();
 }
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index 787006e..5db7b41 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -763,7 +763,7 @@
             if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (sAconfigFlags.skipCurrentElement(parser)) {
+            if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
@@ -842,7 +842,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (sAconfigFlags.skipCurrentElement(parser)) {
+            if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
@@ -988,7 +988,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (sAconfigFlags.skipCurrentElement(parser)) {
+            if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
@@ -1610,7 +1610,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (sAconfigFlags.skipCurrentElement(parser)) {
+            if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
@@ -1853,7 +1853,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (sAconfigFlags.skipCurrentElement(parser)) {
+            if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
                 continue;
             }
             if (parser.getName().equals("intent")) {
@@ -2187,6 +2187,8 @@
                     pkg.setKnownActivityEmbeddingCerts(knownActivityEmbeddingCerts);
                 }
             }
+            pkg.setIntentMatchingFlags(
+                    sa.getInt(R.styleable.AndroidManifestApplication_intentMatchingFlags, 0));
         } finally {
             sa.recycle();
         }
@@ -2202,7 +2204,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (sAconfigFlags.skipCurrentElement(parser)) {
+            if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
@@ -2620,7 +2622,7 @@
                 }
             }
 
-            ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+            ParseResult<String[]> certResult = parseAdditionalCertificates(input, pkg, res, parser);
             if (certResult.isError()) {
                 return input.error(certResult);
             }
@@ -2674,7 +2676,8 @@
             // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
             String[] additionalCertSha256Digests = EmptyArray.STRING;
             if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
-                ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+                ParseResult<String[]> certResult =
+                        parseAdditionalCertificates(input, pkg, res, parser);
                 if (certResult.isError()) {
                     return input.error(certResult);
                 }
@@ -2782,7 +2785,7 @@
     }
 
     private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
-            Resources resources, XmlResourceParser parser)
+            ParsingPackage pkg, Resources resources, XmlResourceParser parser)
             throws XmlPullParserException, IOException {
         String[] certSha256Digests = EmptyArray.STRING;
         final int depth = parser.getDepth();
@@ -2793,7 +2796,7 @@
             if (type != XmlPullParser.START_TAG) {
                 continue;
             }
-            if (sAconfigFlags.skipCurrentElement(parser)) {
+            if (sAconfigFlags.skipCurrentElement(pkg, parser)) {
                 continue;
             }
 
diff --git a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
index febe1f3..70d148a 100644
--- a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java
@@ -132,9 +132,9 @@
     @Deprecated
     @Override
     void dumpViewerConfig() {
-        Log.d(LOG_TAG, "Dumping viewer config to trace");
+        Log.d(LOG_TAG, "Dumping viewer config to trace from " + mViewerConfigFilePath);
         Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider);
-        Log.d(LOG_TAG, "Dumped viewer config to trace");
+        Log.d(LOG_TAG, "Successfully dumped viewer config to trace from " + mViewerConfigFilePath);
     }
 
     @NonNull
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/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/core/java/com/android/internal/telephony/ISatelliteStateChangeListener.aidl
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to core/java/com/android/internal/telephony/ISatelliteStateChangeListener.aidl
index 83cbc52..4d195c2 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/core/java/com/android/internal/telephony/ISatelliteStateChangeListener.aidl
@@ -13,10 +13,9 @@
  * 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.internal.telephony;
+
+oneway interface ISatelliteStateChangeListener {
+    void onSatelliteEnabledStateChanged(boolean isEnabled);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index ca75abd..1c76a6c 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -38,6 +38,7 @@
 import com.android.internal.telephony.ICarrierPrivilegesCallback;
 import com.android.internal.telephony.IPhoneStateListener;
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
+import com.android.internal.telephony.ISatelliteStateChangeListener;
 
 interface ITelephonyRegistry {
     void addOnSubscriptionsChangedListener(String pkg, String featureId,
@@ -124,4 +125,8 @@
     void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active);
     void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible);
     void notifyCarrierRoamingNtnAvailableServicesChanged(int subId, in int[] availableServices);
+
+    void addSatelliteStateChangeListener(ISatelliteStateChangeListener listener, String pkg, String featureId);
+    void removeSatelliteStateChangeListener(ISatelliteStateChangeListener listener, String pkg);
+    void notifySatelliteStateChanged(boolean isEnabled);
 }
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/services/core/java/com/android/server/FgThread.java b/core/java/com/android/server/FgThread.java
similarity index 97%
rename from services/core/java/com/android/server/FgThread.java
rename to core/java/com/android/server/FgThread.java
index b4b6e3f..f8a6bb0 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/core/java/com/android/server/FgThread.java
@@ -30,7 +30,10 @@
  * relatively long-running operations like saving state to disk (in addition to
  * simply being a background priority), which can cause operations scheduled on it
  * to be delayed for a user-noticeable amount of time.
+ *
+ * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public final class FgThread extends ServiceThread {
     private static final long SLOW_DISPATCH_THRESHOLD_MS = 100;
     private static final long SLOW_DELIVERY_THRESHOLD_MS = 200;
diff --git a/services/core/java/com/android/server/ServiceThread.java b/core/java/com/android/server/ServiceThread.java
similarity index 95%
rename from services/core/java/com/android/server/ServiceThread.java
rename to core/java/com/android/server/ServiceThread.java
index 6d8e49c..86e507b 100644
--- a/services/core/java/com/android/server/ServiceThread.java
+++ b/core/java/com/android/server/ServiceThread.java
@@ -24,7 +24,10 @@
 
 /**
  * Special handler thread that we create for system services that require their own loopers.
+ *
+ * @hide
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class ServiceThread extends HandlerThread {
     private static final String TAG = "ServiceThread";
 
diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
index d430fe3..5350059 100644
--- a/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -1514,4 +1514,10 @@
      * @hide
      */
     boolean isAllowCrossUidActivitySwitchFromBelow();
+
+    /**
+     * Returns the intent matching flags.
+     * @hide
+     */
+    int getIntentMatchingFlags();
 }
diff --git a/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java b/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java
similarity index 99%
rename from services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java
rename to core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java
index 1526230..e8aeb86 100644
--- a/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java
+++ b/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java
@@ -63,6 +63,8 @@
  * <p>Optionally, two permissions may be specified: (1) a caller permission - any service that does
  * not require callers to hold this permission is rejected (2) a service permission - any service
  * whose package does not hold this permission is rejected.
+ *
+ * @hide
  */
 public final class CurrentUserServiceSupplier extends BroadcastReceiver implements
         ServiceSupplier<CurrentUserServiceSupplier.BoundServiceInfo> {
diff --git a/services/core/java/com/android/server/servicewatcher/OWNERS b/core/java/com/android/server/servicewatcher/OWNERS
similarity index 100%
rename from services/core/java/com/android/server/servicewatcher/OWNERS
rename to core/java/com/android/server/servicewatcher/OWNERS
diff --git a/services/core/java/com/android/server/servicewatcher/ServiceWatcher.java b/core/java/com/android/server/servicewatcher/ServiceWatcher.java
similarity index 92%
rename from services/core/java/com/android/server/servicewatcher/ServiceWatcher.java
rename to core/java/com/android/server/servicewatcher/ServiceWatcher.java
index 5636718..831ff67 100644
--- a/services/core/java/com/android/server/servicewatcher/ServiceWatcher.java
+++ b/core/java/com/android/server/servicewatcher/ServiceWatcher.java
@@ -16,6 +16,10 @@
 
 package com.android.server.servicewatcher;
 
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_NOT_FOREGROUND;
+import static android.content.Context.BIND_NOT_VISIBLE;
+
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
@@ -52,6 +56,8 @@
  * whether any particular {@link BinderOperation} will succeed. Clients must ensure they do not rely
  * on this, and instead use {@link ServiceListener} notifications as necessary to recover from
  * failures.
+ *
+ * @hide
  */
 public interface ServiceWatcher {
 
@@ -144,6 +150,10 @@
         protected final @Nullable String mAction;
         protected final int mUid;
         protected final ComponentName mComponentName;
+        private final int mFlags;
+
+        private static final int DEFAULT_FLAGS =
+                BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE;
 
         protected BoundServiceInfo(String action, ResolveInfo resolveInfo) {
             this(action, resolveInfo.serviceInfo.applicationInfo.uid,
@@ -151,9 +161,14 @@
         }
 
         protected BoundServiceInfo(String action, int uid, ComponentName componentName) {
+            this(action, uid, componentName, DEFAULT_FLAGS);
+        }
+
+        protected BoundServiceInfo(String action, int uid, ComponentName componentName, int flags) {
             mAction = action;
             mUid = uid;
             mComponentName = Objects.requireNonNull(componentName);
+            mFlags = flags;
         }
 
         /** Returns the action associated with this bound service. */
@@ -171,6 +186,11 @@
             return UserHandle.getUserId(mUid);
         }
 
+        /** Returns flags used when binding the service. */
+        public int getFlags() {
+            return mFlags;
+        }
+
         @Override
         public final boolean equals(Object o) {
             if (this == o) {
@@ -183,12 +203,13 @@
             BoundServiceInfo that = (BoundServiceInfo) o;
             return mUid == that.mUid
                     && Objects.equals(mAction, that.mAction)
-                    && mComponentName.equals(that.mComponentName);
+                    && mComponentName.equals(that.mComponentName)
+                    && mFlags == that.mFlags;
         }
 
         @Override
         public final int hashCode() {
-            return Objects.hash(mAction, mUid, mComponentName);
+            return Objects.hash(mAction, mUid, mComponentName, mFlags);
         }
 
         @Override
@@ -256,4 +277,4 @@
      * Dumps ServiceWatcher information.
      */
     void dump(PrintWriter pw);
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java b/core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java
similarity index 97%
rename from services/core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java
rename to core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java
index b178269..ccbab9f 100644
--- a/services/core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java
+++ b/core/java/com/android/server/servicewatcher/ServiceWatcherImpl.java
@@ -16,10 +16,6 @@
 
 package com.android.server.servicewatcher;
 
-import static android.content.Context.BIND_AUTO_CREATE;
-import static android.content.Context.BIND_NOT_FOREGROUND;
-import static android.content.Context.BIND_NOT_VISIBLE;
-
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
@@ -46,6 +42,8 @@
  * Implementation of ServiceWatcher. Keeping the implementation separate from the interface allows
  * us to store the generic relationship between the service supplier and the service listener, while
  * hiding the generics from clients, simplifying the API.
+ *
+ * @hide
  */
 class ServiceWatcherImpl<TBoundServiceInfo extends BoundServiceInfo> implements ServiceWatcher,
         ServiceChangedListener {
@@ -212,7 +210,7 @@
                     mBoundServiceInfo.getComponentName());
             try {
                 if (!mContext.bindServiceAsUser(bindIntent, this,
-                        BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE,
+                        mBoundServiceInfo.getFlags(),
                         mHandler, UserHandle.of(mBoundServiceInfo.getUserId()))) {
                     Log.e(TAG, "[" + mTag + "] unexpected bind failure - retrying later");
                     mRebinder = this::bind;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index eb07f7c..2bb6e71 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",
@@ -103,18 +105,7 @@
     ],
 
     shared_libs: [
-        "libbase",
-        "libcutils",
         "libtracing_perfetto",
-        "libharfbuzz_ng",
-        "liblog",
-        "libmediautils",
-        "libminikin",
-        "libz",
-        "server_configurable_flags",
-        "libaconfig_storage_read_api_cc",
-        "android.database.sqlite-aconfig-cc",
-        "android.media.audiopolicy-aconfig-cc",
     ],
 
     static_libs: [
@@ -203,7 +194,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",
@@ -251,7 +241,6 @@
                 "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",
@@ -303,6 +292,14 @@
             ],
 
             shared_libs: [
+                "libbase",
+                "libharfbuzz_ng",
+                "liblog",
+                "libmediautils",
+                "libminikin",
+                "libz",
+                "android.database.sqlite-aconfig-cc",
+                "android.media.audiopolicy-aconfig-cc",
                 "audioclient-types-aidl-cpp",
                 "audioflinger-aidl-cpp",
                 "audiopolicy-types-aidl-cpp",
@@ -412,20 +409,24 @@
                 "frameworks/native/libs/nativebase/include",
                 "frameworks/native/libs/nativewindow/include",
             ],
-            shared_libs: [
-                "libicui18n",
-                "libicuuc",
-            ],
             static_libs: [
                 "libandroidfw",
+                "libbase",
                 "libbinary_parse",
+                "libcutils",
                 "libdng_sdk",
                 "libft2",
+                "libharfbuzz_ng",
                 "libhostgraphics",
                 "libhwui",
+                "libicui18n",
+                "libicuuc",
+                "libicuuc_stubdata",
                 "libimage_type_recognition",
                 "libinput",
                 "libjpeg",
+                "liblog",
+                "libminikin",
                 "libnativehelper_jvm",
                 "libpiex",
                 "libpng",
@@ -435,15 +436,21 @@
                 "libwebp-decode",
                 "libwebp-encode",
                 "libwuffs_mirror_release_c",
+                "libz",
                 "libimage_io",
                 "libjpegdecoder",
                 "libjpegencoder",
                 "libultrahdr",
+                "server_configurable_flags",
             ],
+            export_static_lib_headers: [
+                "libnativehelper_jvm",
+                "libui-types",
+            ],
+            stl: "libc++_static",
         },
         host_linux: {
             srcs: [
-                "android_content_res_ApkAssets.cpp",
                 "android_database_CursorWindow.cpp",
                 "android_database_SQLiteCommon.cpp",
                 "android_database_SQLiteConnection.cpp",
@@ -458,7 +465,6 @@
                 "android_view_InputEventReceiver.cpp",
                 "android_view_InputEventSender.cpp",
 
-                "android_util_AssetManager.cpp",
                 "android_util_Binder.cpp",
 
                 "android_util_FileObserver.cpp",
@@ -467,14 +473,18 @@
                 "libbinderthreadstateutils",
                 "libsqlite",
                 "libgui_window_info_static",
-            ],
-            shared_libs: [
-                // libbinder needs to be shared since it has global state
-                // (e.g. gDefaultServiceManager)
                 "libbinder",
                 "libhidlbase", // libhwbinder is in here
             ],
         },
+        linux_glibc_x86_64: {
+            ldflags: ["-static-libgcc"],
+            dist: {
+                targets: ["layoutlib"],
+                dir: "layoutlib_native/linux",
+                tag: "stripped_all",
+            },
+        },
     },
 }
 
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_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 f162b74..df87a69 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -117,6 +117,7 @@
     jfieldID activeDisplayModeId;
     jfieldID renderFrameRate;
     jfieldID hasArrSupport;
+    jfieldID frameRateCategoryRate;
     jfieldID supportedColorModes;
     jfieldID activeColorMode;
     jfieldID hdrCapabilities;
@@ -292,6 +293,16 @@
     jfieldID frameNumber;
 } gStalledTransactionInfoClassInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+} gFrameRateCategoryRateClassInfo;
+
+static struct {
+    jclass clazz;
+    jmethodID asList;
+} gUtilArrays;
+
 constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode) {
     switch (colorMode) {
         case ui::ColorMode::DISPLAY_P3:
@@ -1388,6 +1399,13 @@
     return object;
 }
 
+static jobject convertFrameRateCategoryRateToJavaObject(
+        JNIEnv* env, const ui::FrameRateCategoryRate& frameRateCategoryRate) {
+    return env->NewObject(gFrameRateCategoryRateClassInfo.clazz,
+                          gFrameRateCategoryRateClassInfo.ctor, frameRateCategoryRate.getNormal(),
+                          frameRateCategoryRate.getHigh());
+}
+
 static jobject convertDisplayModeToJavaObject(JNIEnv* env, const ui::DisplayMode& config) {
     jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor);
     env->SetIntField(object, gDisplayModeClassInfo.id, config.id);
@@ -1456,6 +1474,8 @@
                      info.activeDisplayModeId);
     env->SetFloatField(object, gDynamicDisplayInfoClassInfo.renderFrameRate, info.renderFrameRate);
     env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.hasArrSupport, info.hasArrSupport);
+    env->SetObjectField(object, gDynamicDisplayInfoClassInfo.frameRateCategoryRate,
+                        convertFrameRateCategoryRateToJavaObject(env, info.frameRateCategoryRate));
     jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size());
     if (colorModesArray == NULL) {
         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
@@ -2191,10 +2211,13 @@
             env->SetObjectArrayElement(jJankDataArray, i, jJankData);
             env->DeleteLocalRef(jJankData);
         }
-        env->CallVoidMethod(target,
-                gJankDataListenerClassInfo.onJankDataAvailable,
-                jJankDataArray);
+
+        jobject jJankDataList =
+                env->CallStaticObjectMethod(gUtilArrays.clazz, gUtilArrays.asList, jJankDataArray);
         env->DeleteLocalRef(jJankDataArray);
+
+        env->CallVoidMethod(target, gJankDataListenerClassInfo.onJankDataAvailable, jJankDataList);
+        env->DeleteLocalRef(jJankDataList);
         env->DeleteLocalRef(target);
 
         return true;
@@ -2666,6 +2689,15 @@
             GetFieldIDOrDie(env, dynamicInfoClazz, "renderFrameRate", "F");
     gDynamicDisplayInfoClassInfo.hasArrSupport =
             GetFieldIDOrDie(env, dynamicInfoClazz, "hasArrSupport", "Z");
+
+    gDynamicDisplayInfoClassInfo.frameRateCategoryRate =
+            GetFieldIDOrDie(env, dynamicInfoClazz, "frameRateCategoryRate",
+                            "Landroid/view/FrameRateCategoryRate;");
+    jclass frameRateCategoryRateClazz = FindClassOrDie(env, "android/view/FrameRateCategoryRate");
+    gFrameRateCategoryRateClassInfo.clazz = MakeGlobalRefOrDie(env, frameRateCategoryRateClazz);
+    gFrameRateCategoryRateClassInfo.ctor =
+            GetMethodIDOrDie(env, frameRateCategoryRateClazz, "<init>", "(FF)V");
+
     gDynamicDisplayInfoClassInfo.supportedColorModes =
             GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I");
     gDynamicDisplayInfoClassInfo.activeColorMode =
@@ -2834,7 +2866,7 @@
     gJankDataListenerClassInfo.clazz = MakeGlobalRefOrDie(env, onJankDataListenerClazz);
     gJankDataListenerClassInfo.onJankDataAvailable =
             GetMethodIDOrDie(env, onJankDataListenerClazz, "onJankDataAvailable",
-                             "([Landroid/view/SurfaceControl$JankData;)V");
+                             "(Ljava/util/List;)V");
 
     jclass transactionCommittedListenerClazz =
             FindClassOrDie(env, "android/view/SurfaceControl$TransactionCommittedListener");
@@ -2909,6 +2941,10 @@
     gStalledTransactionInfoClassInfo.frameNumber =
             GetFieldIDOrDie(env, stalledTransactionInfoClazz, "frameNumber", "J");
 
+    jclass utilArrays = FindClassOrDie(env, "java/util/Arrays");
+    gUtilArrays.clazz = MakeGlobalRefOrDie(env, utilArrays);
+    gUtilArrays.asList = GetStaticMethodIDOrDie(env, utilArrays, "asList",
+                                                "([Ljava/lang/Object;)Ljava/util/List;");
     return err;
 }
 
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/proto/android/service/appwidget.proto b/core/proto/android/service/appwidget.proto
index fb90719..97350ef 100644
--- a/core/proto/android/service/appwidget.proto
+++ b/core/proto/android/service/appwidget.proto
@@ -20,8 +20,6 @@
 option java_multiple_files = true;
 option java_outer_classname = "AppWidgetServiceProto";
 
-import "frameworks/base/core/proto/android/widget/remoteviews.proto";
-
 // represents the object holding the dump info of the app widget service
 message AppWidgetServiceDumpProto {
     repeated WidgetProto widgets = 1; // the array of bound widgets
@@ -40,14 +38,3 @@
     optional int32 maxHeight = 9;
     optional bool restoreCompleted = 10;
 }
-
-// represents a set of widget previews for a particular provider
-message GeneratedPreviewsProto {
-    repeated Preview previews = 1;
-
-    // represents a particular RemoteViews preview, which may be set for multiple categories
-    message Preview {
-        repeated int32 widget_categories = 1;
-        optional android.widget.RemoteViewsProto views = 2;
-    }
-}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4c38246..0479318 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" />
 
 
     <!-- ====================================================================== -->
@@ -7022,7 +7023,6 @@
     <!-- Allows an application to set the advanced features on BiometricDialog (SystemUI), including
          logo, logo description, and content view with more options button.
          <p>Not for use by third-party applications.
-         @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")
     -->
     <permission android:name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED"
                 android:protectionLevel="signature|privileged" />
@@ -8535,10 +8535,26 @@
         @hide
     -->
     <permission
-        android:name="android.permission.RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
+        android:name="android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
         android:protectionLevel="internal"
         android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/>
 
+    <uses-permission
+        android:name="android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
+        android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/>
+
+    <!-- @SystemApi
+        @FlaggedApi("android.media.tv.flags.kids_mode_tvdb_sharing")
+        This permission is required when accessing information related to
+        singleUser-ed TIS session.
+        <p>This should only be used by OEM.
+        <p>Protection level: signature|privileged|vendorPrivileged
+        @hide
+    -->
+    <permission android:name="android.permission.SINGLE_USER_TIS_ACCESS"
+        android:protectionLevel="signature|privileged|vendorPrivileged"
+        android:featureFlag="android.media.tv.flags.kids_mode_tvdb_sharing"/>
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/layout/app_language_picker_current_locale_item.xml b/core/res/res/layout/app_language_picker_locale_item.xml
similarity index 73%
rename from core/res/res/layout/app_language_picker_current_locale_item.xml
rename to core/res/res/layout/app_language_picker_locale_item.xml
index edd6d64..bcad9ce 100644
--- a/core/res/res/layout/app_language_picker_current_locale_item.xml
+++ b/core/res/res/layout/app_language_picker_locale_item.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2022 The Android Open Source Project
+  ~ Copyright (C) 2024 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -20,10 +20,27 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:gravity="center_vertical">
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:minHeight="?android:attr/listPreferredItemHeight"
+        android:layout_marginStart="20dip">
+
+      <RadioButton
+          android:id="@+id/checkbox"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_gravity="center"
+          android:background="@null"
+          android:focusable="false"
+          android:clickable="false" />
+
+    </LinearLayout>
+
     <RelativeLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="6dip"
         android:layout_marginTop="6dip"
         android:layout_marginBottom="6dip"
         android:layout_weight="1">
@@ -31,20 +48,4 @@
             android:id="@+id/language_picker_item"
             layout="@layout/language_picker_item" />
     </RelativeLayout>
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="center"
-        android:minHeight="?android:attr/listPreferredItemHeight">
-        <ImageView
-            android:id="@+id/imageView"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
-            android:layout_marginHorizontal="16dp"
-            android:src="@drawable/ic_check_24dp"
-            app:tint="?attr/colorAccentPrimaryVariant"
-            android:contentDescription="@*android:string/checked"/>
-    </LinearLayout>
 </LinearLayout>
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..3fac736 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1943,6 +1943,7 @@
     <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>
+    <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> to <xliff:g id="END">%2$s</xliff:g>"</string>
     <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 +2425,10 @@
     <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>
+    <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Turn on \"Automatically select network\""</string>
+    <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Turn on \"Automatically select network\" in Settings so your phone can find a network that works with satellite"</string>
+    <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Turn on"</string>
+    <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Go back"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
     <string name="fingerprint_dangling_notification_msg_1" msgid="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..4070f11 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,7 @@
     <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>
+    <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"Da <xliff:g id="START">%1$s</xliff:g> a <xliff:g id="END">%2$s</xliff:g>"</string>
     <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 +2426,10 @@
     <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>
+    <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Attiva \"Seleziona rete automaticamente\""</string>
+    <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Attiva \"Seleziona rete automaticamente\" nelle Impostazioni in modo che lo smartphone possa trovare una rete compatibile con il satellite"</string>
+    <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Attiva"</string>
+    <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Indietro"</string>
     <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..5684d78 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -590,7 +590,7 @@
     <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"तपाईँको फोन मात्र होइन, मल्टिकास्ट ठेगानाहरूको प्रयोग गरे Wi-Fi नेटवर्कका सबै उपकरणहरूमा पठाइएका प्याकेटहरू प्राप्त गर्न एपलाई अनुमति दिन्छ। यसले गैर-मल्टिकास्ट मोडभन्दा बढी उर्जा प्रयोग गर्छ।"</string>
     <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"ब्लुटुथ सेटिङहरूमा पहुँच गर्नुहोस्"</string>
     <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"स्थानीय ब्लुटुथ ट्याब्लेटलाई कन्फिगर गर्नको लागि र टाढाका उपकरणहरूलाई पत्ता लगाउन र जोड्नको लागि एपलाई अनुमति दिन्छ।"</string>
-    <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"एपलाई तपाईंको Android टिभी डिभाइसको ब्लुटुथ कन्फिगर गर्ने तथा टाढा रहेका यन्त्रहरू पत्ता लगाई ती यन्त्रहरूसँग जोडा बनाउने अनुमति दिन्छ।"</string>
+    <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"एपलाई तपाईंको Android टिभी डिभाइसको ब्लुटुथ कन्फिगर गर्ने तथा टाढा रहेका यन्त्रहरू पत्ता लगाई ती यन्त्रहरूसँग कनेक्ट गर्ने अनुमति दिन्छ।"</string>
     <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"एपलाई स्थानीय ब्लुटुथ फोन कन्फिगर गर्न र टाढाका उपकरणहरूसँग खोज गर्न र जोडी गर्न अनुमति दिन्छ।"</string>
     <string name="permlab_accessWimaxState" msgid="7029563339012437434">"WiMAXसँग जोड्नुहोस् वा छुटाउनुहोस्"</string>
     <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"एपलाई वाइम्याक्स सक्षम छ कि छैन र जडान भएको कुनै पनि वाइम्याक्स नेटवर्कहरूको बारेमा जानकारी निर्धारिण गर्न अनुमति दिन्छ।"</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-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..86e0fcd 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1944,6 +1944,7 @@
     <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>
+    <string name="zen_mode_trigger_summary_range_words" msgid="7228261413029290750">"<xliff:g id="START">%1$s</xliff:g> – <xliff:g id="END">%2$s</xliff:g>"</string>
     <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 +2426,10 @@
     <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>
+    <string name="satellite_manual_selection_state_popup_title" msgid="8545991934926661974">"Ative a opção \"Selecionar rede automaticamente\""</string>
+    <string name="satellite_manual_selection_state_popup_message" msgid="1928101658551382450">"Ative a opção \"Selecionar rede automaticamente\" nas Definições para que o telemóvel possa encontrar uma rede que funcione com o satélite"</string>
+    <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ativar"</string>
+    <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Retroceder"</string>
     <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..1d9e00c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1937,12 +1937,14 @@
     <string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"వారపు రోజుల్లో రాత్రి"</string>
     <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"వారాంతం"</string>
     <string name="zen_mode_default_events_name" msgid="2280682960128512257">"ఈవెంట్"</string>
-    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"నిద్ర"</string>
+    <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"స్లీప్ మోడ్"</string>
     <string name="zen_mode_implicit_trigger_description" msgid="5714956693073007111">"<xliff:g id="APP_NAME">%1$s</xliff:g> ద్వారా మేనేజ్ చేయబడుతోంది"</string>
     <string name="zen_mode_implicit_activated" msgid="2634285680776672994">"ఆన్‌లో ఉంది"</string>
     <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-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 d750ff6..092d2a72 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3486,6 +3486,12 @@
              representation this attribute can be used for providing such. -->
         <attr name="contentDescription" format="string" localization="suggested" />
 
+        <!-- Provides brief supplemental information for the view, such as the purpose of
+             the view when that purpose is not conveyed within its textual representation.
+             This property is used primarily for accessibility. -->
+        <!-- @FlaggedApi("android.view.accessibility.supplemental_description") -->
+        <attr name="supplementalDescription" format="string" localization="suggested" />
+
         <!-- Sets the id of a view that screen readers are requested to visit after this view.
              Requests that a screen-reader visits the content of this view before the content of the
              one it precedes. This does nothing if either view is not important for accessibility.
@@ -9238,6 +9244,8 @@
             <flag name="home_screen" value="0x1" />
             <flag name="keyguard" value="0x2" />
             <flag name="searchbox" value="0x4" />
+            <!-- @FlaggedApi("android.appwidget.flags.not_keyguard_category") -->
+            <flag name="not_keyguard" value="0x8" />
         </attr>
         <!-- Flags indicating various features supported by the widget. These are hints to the
          widget host, and do not actually change the behavior of the widget. -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 0be33c2..4d73f22 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1876,6 +1876,34 @@
          @hide @SystemApi @FlaggedApi("android.content.res.manifest_flagging") -->
     <attr name="featureFlag" format="string" />
 
+    <!-- This attribute provides a way to fine-tune how incoming intents are matched to application
+    components. By default, no special matching rules are applied. This attribute can be specified
+    on the {@code <application>} tag as well as at the component tags such as {@code <activity>},
+    {@code <activity-alias>}, {@code <receiver>}, {@code <service>}, {@code <provider>} and the
+    attribute on the component can be used to override what's on the {@code <application>} tag. -->
+    <attr name="intentMatchingFlags">
+        <!-- Disables all special matching rules for incoming intents. When specifying multiple
+        flags, conflicting values are resolved by giving precedence to the "none" flag. -->
+        <flag name="none" value="0x0001" />
+
+        <!-- Enforces stricter matching for incoming intents:
+        <ul>
+             <li>Explicit intents should match the target component's intent filter
+             <li>Intents without an action should not match any intent filter
+        </ul>
+        -->
+        <flag name="enforceIntentFilter" value="0x0002" />
+
+        <!-- Relaxes the matching rules to allow intents without a action to match. This flag to be
+        used in conjunction with enforceIntentFilter to achieve a specific behavior:
+        <ul>
+            <li>Explicit intents should match the target component's intent filter
+            <li>Intents without an action are allowed to match any intent filter
+        </ul>
+        -->
+        <flag name="allowNullAction" value="0x0004" />
+    </attr>
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -2243,6 +2271,8 @@
              {@link android.window.OnBackInvokedCallback#onBackInvoked
              OnBackInvokedCallback.onBackInvoked()} on the focused window. -->
         <attr name="enableOnBackInvokedCallback" format="boolean"/>
+
+        <attr name="intentMatchingFlags"/>
     </declare-styleable>
 
     <!-- An attribution is a logical part of an app and is identified by a tag.
@@ -2930,6 +2960,7 @@
              contained here. -->
         <attr name="attributionTags" />
         <attr name="systemUserOnly" format="boolean" />
+        <attr name="intentMatchingFlags"/>
     </declare-styleable>
 
     <!-- Attributes that can be supplied in an AndroidManifest.xml
@@ -3089,6 +3120,7 @@
         -->
         <attr name="allowSharedIsolatedProcess" format="boolean" />
         <attr name="systemUserOnly" format="boolean" />
+        <attr name="intentMatchingFlags"/>
     </declare-styleable>
 
     <!-- @hide The <code>apex-system-service</code> tag declares an apex system service
@@ -3156,6 +3188,7 @@
              Context.createAttributionContext() using the first attribution tag
              contained here. -->
         <attr name="attributionTags" />
+        <attr name="intentMatchingFlags"/>
     </declare-styleable>
 
     <!-- The <code>activity</code> tag declares an
@@ -3350,6 +3383,7 @@
                  URIs. -->
             <enum name="readAndWrite" value="4" />
         </attr>
+        <attr name="intentMatchingFlags"/>
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
@@ -3392,6 +3426,7 @@
         <attr name="attributionTags" />
         <attr name="allowUntrustedActivityEmbedding" />
         <attr name="knownActivityEmbeddingCerts" />
+        <attr name="intentMatchingFlags"/>
     </declare-styleable>
 
     <!-- The <code>meta-data</code> tag is used to attach additional
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/public-staging.xml b/core/res/res/values/public-staging.xml
index 8121545..0c28ea4 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -121,6 +121,10 @@
     <!-- @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled")
          @hide @SystemApi -->
     <public name="backgroundPermission"/>
+    <!-- @FlaggedApi(android.view.accessibility.supplemental_description) -->
+    <public name="supplementalDescription"/>
+    <!-- @FlaggedApi("android.security.enable_intent_matching_flags") -->
+    <public name="intentMatchingFlags"/>
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01b60000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 732c316..d3ef07ca 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5337,7 +5337,9 @@
     <!-- Zen mode - name of default automatic calendar time-based rule that is triggered every night (when sleeping). [CHAR LIMIT=40] -->
     <string name="zen_mode_default_every_night_name">Sleeping</string>
 
-    <!-- Zen mode - Trigger description of the rule, indicating which app owns it. [CHAR_LIMIT=100] -->
+    <!-- Implicit zen mode - Name of the rule, indicating which app owns it. [CHAR_LIMIT=30] -->
+    <string name="zen_mode_implicit_name">Do Not Disturb (<xliff:g id="app_name" example="Gmail">%1$s</xliff:g>)</string>
+    <!-- Implicit zen mode - Trigger description of the rule, indicating which app owns it. [CHAR_LIMIT=100] -->
     <string name="zen_mode_implicit_trigger_description">Managed by <xliff:g id="app_name">%1$s</xliff:g></string>
     <!-- Zen mode - Condition summary when a rule is activated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] -->
     <string name="zen_mode_implicit_activated">On</string>
@@ -6510,6 +6512,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/symbols.xml b/core/res/res/values/symbols.xml
index 4dbeb2f..515ebd5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2652,6 +2652,7 @@
   <java-symbol type="string" name="zen_mode_default_weekends_name" />
   <java-symbol type="string" name="zen_mode_default_events_name" />
   <java-symbol type="string" name="zen_mode_default_every_night_name" />
+  <java-symbol type="string" name="zen_mode_implicit_name" />
   <java-symbol type="string" name="zen_mode_implicit_trigger_description" />
   <java-symbol type="string" name="zen_mode_implicit_activated" />
   <java-symbol type="string" name="zen_mode_implicit_deactivated" />
@@ -3989,6 +3990,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" />
@@ -5262,7 +5264,7 @@
   <java-symbol type="string" name="system_locale_title" />
   <java-symbol type="layout" name="app_language_picker_system_default" />
   <java-symbol type="layout" name="app_language_picker_system_current" />
-  <java-symbol type="layout" name="app_language_picker_current_locale_item" />
+  <java-symbol type="layout" name="app_language_picker_locale_item" />
   <java-symbol type="id" name="system_locale_subtitle" />
   <java-symbol type="id" name="language_picker_item" />
   <java-symbol type="id" name="language_picker_header" />
@@ -5543,6 +5545,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" />
 
@@ -5572,6 +5578,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/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index bdc4d25..0f8dc13 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -20,13 +20,26 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 
+import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
+import android.hardware.broadcastradio.Alert;
+import android.hardware.broadcastradio.AlertArea;
+import android.hardware.broadcastradio.AlertCategory;
+import android.hardware.broadcastradio.AlertCertainty;
+import android.hardware.broadcastradio.AlertInfo;
+import android.hardware.broadcastradio.AlertMessageType;
+import android.hardware.broadcastradio.AlertSeverity;
+import android.hardware.broadcastradio.AlertStatus;
+import android.hardware.broadcastradio.AlertUrgency;
 import android.hardware.broadcastradio.AmFmBandRange;
 import android.hardware.broadcastradio.AmFmRegionConfig;
 import android.hardware.broadcastradio.ConfigFlag;
+import android.hardware.broadcastradio.Coordinate;
 import android.hardware.broadcastradio.DabTableEntry;
+import android.hardware.broadcastradio.Geocode;
 import android.hardware.broadcastradio.IdentifierType;
 import android.hardware.broadcastradio.Metadata;
+import android.hardware.broadcastradio.Polygon;
 import android.hardware.broadcastradio.ProgramFilter;
 import android.hardware.broadcastradio.ProgramIdentifier;
 import android.hardware.broadcastradio.ProgramInfo;
@@ -37,10 +50,13 @@
 import android.hardware.radio.Flags;
 import android.hardware.radio.ProgramList;
 import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioAlert;
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioMetadata;
 import android.hardware.radio.UniqueProgramIdentifier;
 import android.os.ServiceSpecificException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -148,6 +164,9 @@
     private static final ProgramIdentifier TEST_HAL_HD_STATION_LOCATION_ID =
             AidlTestUtils.makeHalIdentifier(IdentifierType.HD_STATION_LOCATION,
                     TEST_HD_LOCATION_VALUE);
+    private static final ProgramIdentifier TEST_HAL_HD_FM_FREQUENCY_ID =
+            AidlTestUtils.makeHalIdentifier(IdentifierType.AMFM_FREQUENCY_KHZ,
+                    TEST_HD_FREQUENCY_VALUE);
 
     private static final UniqueProgramIdentifier TEST_DAB_UNIQUE_ID = new UniqueProgramIdentifier(
             TEST_DAB_SELECTOR);
@@ -173,6 +192,57 @@
     private static final Metadata TEST_HAL_HD_SUBCHANNELS = Metadata.hdSubChannelsAvailable(
             TEST_HD_SUBCHANNELS);
 
+    private static final int TEST_STATUS = RadioAlert.STATUS_ACTUAL;
+    private static final int TEST_HAL_STATUS = AlertStatus.ACTUAL;
+    private static final int TEST_TYPE = RadioAlert.MESSAGE_TYPE_ALERT;
+    private static final int TEST_HAL_TYPE = AlertMessageType.ALERT;
+    private static final int[] TEST_CATEGORY_ARRAY = new int[]{RadioAlert.CATEGORY_CBRNE,
+            RadioAlert.CATEGORY_GEO};
+    private static final int[] TEST_HAL_CATEGORY_LIST = new int[]{AlertCategory.CBRNE,
+            AlertCategory.GEO};
+    private static final int TEST_URGENCY = RadioAlert.URGENCY_FUTURE;
+    private static final int TEST_HAL_URGENCY = AlertUrgency.FUTURE;
+    private static final int TEST_SEVERITY = RadioAlert.SEVERITY_MINOR;
+    private static final int TEST_HAL_SEVERITY = AlertSeverity.MINOR;
+    private static final int TEST_CERTAINTY = RadioAlert.CERTAINTY_UNLIKELY;
+    private static final int TEST_HAL_CERTAINTY = AlertCertainty.UNLIKELY;
+    private static final String TEST_DESCRIPTION_MESSAGE = "Test Alert Description Message.";
+    private static final String TEST_GEOCODE_VALUE_NAME = "ZIP";
+    private static final String TEST_GEOCODE_VALUE_1 = "10001";
+    private static final String TEST_GEOCODE_VALUE_2 = "10002";
+    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 Polygon TEST_HAL_POLYGON = createHalPolygon(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 = new RadioAlert.AlertArea(
+            List.of(TEST_POLYGON), List.of(TEST_GEOCODE_1, TEST_GEOCODE_2));
+    private static final AlertArea TEST_HAL_ALERT_AREA = createHalAlertArea(
+            new Polygon[]{TEST_HAL_POLYGON}, new Geocode[]{
+                    createHalGeocode(TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_1),
+                    createHalGeocode(TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_2)});
+    private static final String TEST_LANGUAGE = "en-US";
+
+    private static final RadioAlert.AlertInfo TEST_ALERT_INFO_1 = new RadioAlert.AlertInfo(
+            TEST_CATEGORY_ARRAY, TEST_URGENCY, TEST_SEVERITY, TEST_CERTAINTY,
+            TEST_DESCRIPTION_MESSAGE, List.of(TEST_AREA), TEST_LANGUAGE);
+    private static final AlertInfo TEST_HAL_ALERT_INFO = createHalAlertInfo(
+            TEST_HAL_CATEGORY_LIST, TEST_HAL_URGENCY, TEST_HAL_SEVERITY, TEST_HAL_CERTAINTY,
+            TEST_DESCRIPTION_MESSAGE, new AlertArea[]{TEST_HAL_ALERT_AREA}, TEST_LANGUAGE);
+    private static final RadioAlert TEST_ALERT = new RadioAlert(TEST_STATUS, TEST_TYPE,
+            List.of(TEST_ALERT_INFO_1));
+    private static final Alert TEST_HAL_ALERT = createHalAlert(TEST_HAL_STATUS, TEST_HAL_TYPE,
+            new AlertInfo[]{TEST_HAL_ALERT_INFO});
+
     @Rule
     public final Expect expect = Expect.create();
     @Rule
@@ -576,6 +646,42 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+    public void programInfoFromHalProgramInfo_withAlertMessageAndFlagEnabled() {
+        android.hardware.broadcastradio.ProgramSelector halHdSelector =
+                AidlTestUtils.makeHalSelector(TEST_HAL_HD_STATION_EXT_ID,
+                        new ProgramIdentifier[]{});
+        ProgramInfo halHdProgramInfo = AidlTestUtils.makeHalProgramInfo(halHdSelector,
+                TEST_HAL_HD_STATION_EXT_ID, TEST_HAL_HD_FM_FREQUENCY_ID, TEST_SIGNAL_QUALITY,
+                new ProgramIdentifier[]{}, new Metadata[]{});
+        halHdProgramInfo.emergencyAlert = TEST_HAL_ALERT;
+
+        RadioManager.ProgramInfo programInfo =
+                ConversionUtils.programInfoFromHalProgramInfo(halHdProgramInfo);
+
+        expect.withMessage("Alert of converted HD program info with alert and enabled flag")
+                .that(programInfo.getAlert()).isEqualTo(TEST_ALERT);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+    public void programInfoFromHalProgramInfo_withAlertMessageAndFlagDisabled() {
+        android.hardware.broadcastradio.ProgramSelector halHdSelector =
+                AidlTestUtils.makeHalSelector(TEST_HAL_HD_STATION_EXT_ID,
+                        new ProgramIdentifier[]{});
+        ProgramInfo halHdProgramInfo = AidlTestUtils.makeHalProgramInfo(halHdSelector,
+                TEST_HAL_HD_STATION_EXT_ID, TEST_HAL_HD_FM_FREQUENCY_ID, TEST_SIGNAL_QUALITY,
+                new ProgramIdentifier[]{}, new Metadata[]{});
+        halHdProgramInfo.emergencyAlert = TEST_HAL_ALERT;
+
+        RadioManager.ProgramInfo programInfo =
+                ConversionUtils.programInfoFromHalProgramInfo(halHdProgramInfo);
+
+        expect.withMessage("Alert of converted HD program info with alert and disabled flag")
+                .that(programInfo.getAlert()).isNull();
+    }
+
+    @Test
     public void tunedProgramInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
         android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
                 AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
@@ -851,7 +957,7 @@
     }
 
     @Test
-    public void radioMetadataFromHalMetadata_withHdMedatadataAndFlagEnabled() {
+    public void radioMetadataFromHalMetadata_withHdMetadataAndFlagEnabled() {
         mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
         String genreValue = "genreTest";
         String commentShortDescriptionValue = "commentShortDescriptionTest";
@@ -973,6 +1079,57 @@
                 .isEmpty();
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+    public void radioAlertFromHalAlert() {
+        RadioAlert convertedAlert = ConversionUtils.radioAlertFromHalAlert(TEST_HAL_ALERT);
+
+        expect.withMessage("Converted alert").that(convertedAlert)
+                .isEqualTo(TEST_ALERT);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+    public void radioAlertFromHalAlert_withLowThanFourCoordinates() {
+        Polygon invalidPolygon = createHalPolygon(List.of(
+                TEST_POLYGON_COORDINATE_START, new RadioAlert.Coordinate(38.34, -119.95),
+                TEST_POLYGON_COORDINATE_START));
+        AlertInfo halAlertInfo = createHalAlertInfo(TEST_HAL_CATEGORY_LIST, TEST_HAL_URGENCY,
+                TEST_HAL_SEVERITY, TEST_HAL_CERTAINTY, TEST_DESCRIPTION_MESSAGE,
+                new AlertArea[]{createHalAlertArea(new Polygon[]{invalidPolygon},
+                        new Geocode[]{})}, TEST_LANGUAGE);
+        Alert halAlert = createHalAlert(TEST_HAL_STATUS, TEST_HAL_TYPE,
+                new AlertInfo[]{halAlertInfo });
+
+        RadioAlert convertedAlert = ConversionUtils.radioAlertFromHalAlert(halAlert);
+
+        expect.withMessage("Empty polygon list with less than 4 coordinates")
+                .that(convertedAlert.getInfoList().get(0).getAreas().get(0).getPolygons())
+                .isEmpty();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+    public void radioAlertFromHalAlert_withDifferentFirstAndLastCoordinate() {
+        Polygon invalidPolygon = createHalPolygon(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),
+                new RadioAlert.Coordinate(38.42, -120.14)));
+        AlertInfo halAlertInfo = createHalAlertInfo(TEST_HAL_CATEGORY_LIST, TEST_HAL_URGENCY,
+                TEST_HAL_SEVERITY, TEST_HAL_CERTAINTY, TEST_DESCRIPTION_MESSAGE,
+                new AlertArea[]{createHalAlertArea(new Polygon[]{invalidPolygon},
+                        new Geocode[]{})}, TEST_LANGUAGE);
+        Alert halAlert = createHalAlert(TEST_HAL_STATUS, TEST_HAL_TYPE,
+                new AlertInfo[]{halAlertInfo});
+
+        RadioAlert convertedAlert = ConversionUtils.radioAlertFromHalAlert(halAlert);
+
+        expect.withMessage("Empty polygon list with different first and last coordinates")
+                .that(convertedAlert.getInfoList().get(0).getAreas().get(0).getPolygons())
+                .isEmpty();
+    }
+
     private static RadioManager.ModuleProperties createModuleProperties() {
         AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
         DabTableEntry[] dabTableEntries = new DabTableEntry[]{
@@ -1028,14 +1185,62 @@
         return halProperties;
     }
 
-    private ProgramSelector.Identifier createHdStationLocationIdWithFlagEnabled() {
+    private static ProgramSelector.Identifier createHdStationLocationIdWithFlagEnabled() {
         return new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_HD_STATION_LOCATION,
                 TEST_HD_LOCATION_VALUE);
     }
 
-    private ProgramSelector createHdSelectorWithFlagEnabled() {
+    private static ProgramSelector createHdSelectorWithFlagEnabled() {
         return new ProgramSelector(ProgramSelector.PROGRAM_TYPE_FM_HD, TEST_HD_STATION_EXT_ID,
                 new ProgramSelector.Identifier[]{createHdStationLocationIdWithFlagEnabled()},
                 /* vendorIds= */ null);
     }
+
+    private static Alert createHalAlert(int status, int messageType, AlertInfo[] alertInfos) {
+        Alert halAlert = new Alert();
+        halAlert.status = status;
+        halAlert.messageType = messageType;
+        halAlert.infoArray = alertInfos;
+        return halAlert;
+    }
+
+    private static AlertInfo createHalAlertInfo(int[] categoryArray, int urgency, int severity,
+            int certainty, String description, AlertArea[] areas, @Nullable String language) {
+        AlertInfo info = new AlertInfo();
+        info.categoryArray = categoryArray;
+        info.urgency = urgency;
+        info.severity = severity;
+        info.certainty = certainty;
+        info.description = description;
+        info.areas = areas;
+        info.language = language;
+        return info;
+    }
+
+    private static AlertArea createHalAlertArea(Polygon[] polygons, Geocode[] geocodes) {
+        AlertArea area = new AlertArea();
+        area.polygons = polygons;
+        area.geocodes = geocodes;
+        return area;
+    }
+
+    private static Polygon createHalPolygon(List<RadioAlert.Coordinate> coordinates) {
+        Coordinate[] halCoordinates = new Coordinate[coordinates.size()];
+        for (int idx = 0; idx < coordinates.size(); idx++) {
+            Coordinate halCoordinate = new Coordinate();
+            halCoordinate.latitude = coordinates.get(idx).getLatitude();
+            halCoordinate.longitude = coordinates.get(idx).getLongitude();
+            halCoordinates[idx] = halCoordinate;
+        }
+        Polygon polygon = new Polygon();
+        polygon.coordinates = halCoordinates;
+        return polygon;
+    }
+
+    private static Geocode createHalGeocode(String valueName, String value) {
+        Geocode halGeocode = new Geocode();
+        halGeocode.valueName = valueName;
+        halGeocode.value = value;
+        return halGeocode;
+    }
 }
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index aee1c3b..a382d79 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -150,6 +150,7 @@
         ":HelloWorldUsingSdk1AndSdk1",
         ":HelloWorldUsingSdk1And2",
         ":HelloWorldUsingSdkMalformedNegativeVersion",
+        ":CtsStaticSharedLibConsumerApp1",
     ],
 }
 
@@ -266,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 3bc8172..3f7c83a 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -39,6 +39,8 @@
             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">
diff --git a/core/tests/coretests/AppThatUsesAppOps/src/android/app/appops/appthatusesappops/AppOpsUserService.kt b/core/tests/coretests/AppThatUsesAppOps/src/android/app/appops/appthatusesappops/AppOpsUserService.kt
index 48053c1..c5f07ff 100644
--- a/core/tests/coretests/AppThatUsesAppOps/src/android/app/appops/appthatusesappops/AppOpsUserService.kt
+++ b/core/tests/coretests/AppThatUsesAppOps/src/android/app/appops/appthatusesappops/AppOpsUserService.kt
@@ -22,7 +22,9 @@
 import android.app.Service
 import android.app.SyncNotedAppOp
 import android.content.Intent
+import android.os.Handler
 import android.os.IBinder
+import android.os.Looper
 import android.util.Log
 import com.android.frameworks.coretests.aidl.IAppOpsUserClient
 import com.android.frameworks.coretests.aidl.IAppOpsUserService
@@ -71,6 +73,7 @@
     override fun onBind(intent: Intent?): IBinder {
         return object : IAppOpsUserService.Stub() {
             private val appOpsManager = getSystemService(AppOpsManager::class.java)!!
+            private val handler = Handler(Looper.getMainLooper())
 
             // Collected note-op calls inside of this process
             private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
@@ -182,6 +185,18 @@
                 }
             }
 
+            override fun callFreezeAndNoteSyncOp(client: IAppOpsUserClient) {
+                handler.post {
+                    client.freezeAndNoteSyncOp()
+                }
+            }
+
+            override fun assertEmptyAsyncNoted() {
+                forwardThrowableFrom {
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
             override fun callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(
                 client: IAppOpsUserClient
             ) {
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserClient.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserClient.aidl
index 68b393c0..11300b0 100644
--- a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserClient.aidl
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserClient.aidl
@@ -20,6 +20,7 @@
     void noteSyncOpNative();
     void noteNonPermissionSyncOpNative();
     oneway void noteSyncOpOnewayNative();
+    void freezeAndNoteSyncOp();
     void noteSyncOpOtherUidNative();
     void noteAsyncOpNative();
     void noteAsyncOpNativeWithCustomMessage();
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserService.aidl
index f5673c4..c6dc7b0 100644
--- a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserService.aidl
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserService.aidl
@@ -25,4 +25,6 @@
     void callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(in IAppOpsUserClient client);
     void callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(in IAppOpsUserClient client);
     void callApiThatNotesAsyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+    void callFreezeAndNoteSyncOp(in IAppOpsUserClient client);
+    void assertEmptyAsyncNoted();
 }
diff --git a/core/tests/coretests/res/xml/livewallpaper.xml b/core/tests/coretests/res/xml/livewallpaper.xml
new file mode 100644
index 0000000..3b3f4a7
--- /dev/null
+++ b/core/tests/coretests/res/xml/livewallpaper.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<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/AppOpsLoggingTest.kt b/core/tests/coretests/src/android/app/AppOpsLoggingTest.kt
index a10d6a9..ff4e532 100644
--- a/core/tests/coretests/src/android/app/AppOpsLoggingTest.kt
+++ b/core/tests/coretests/src/android/app/AppOpsLoggingTest.kt
@@ -33,6 +33,9 @@
 import android.os.Looper
 import android.os.Process
 import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.util.Log
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.frameworks.coretests.aidl.IAppOpsUserClient
@@ -41,9 +44,15 @@
 import org.junit.After
 import org.junit.Assert.fail
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import java.util.concurrent.CompletableFuture
+import java.util.concurrent.LinkedBlockingQueue
 import java.util.concurrent.TimeUnit.MILLISECONDS
+import java.util.concurrent.TimeUnit
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.TimeSource
 
 private const val LOG_TAG = "AppOpsLoggingTest"
 
@@ -71,8 +80,11 @@
 
     private var wasLocationEnabled = false
 
+    @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
     private lateinit var testService: IAppOpsUserService
     private lateinit var serviceConnection: ServiceConnection
+    private lateinit var freezingTestCompletion: CompletableFuture<Unit>
 
     // Collected note-op calls inside of this process
     private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
@@ -123,6 +135,7 @@
 
         context.bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE)
         testService = newService.get(TIMEOUT_MILLIS, MILLISECONDS)
+        freezingTestCompletion = CompletableFuture<Unit>()
     }
 
     private fun clearCollectedNotedOps() {
@@ -253,6 +266,26 @@
         }
     }
 
+    @Test
+    @RequiresFlagsEnabled(android.os.Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK,
+            android.permission.flags.Flags.FLAG_USE_FROZEN_AWARE_REMOTE_CALLBACK_LIST)
+    fun dropAsyncOpNotedWhenFrozen() {
+        // Here's what the test does:
+        // 1. AppOpsLoggingTest calls AppOpsUserService
+        // 2. AppOpsUserService calls freezeAndNoteSyncOp in AppOpsLoggingTest
+        // 3. freezeAndNoteSyncOp freezes AppOpsUserService
+        // 4. freezeAndNoteSyncOp calls nativeNoteOp which leads to an async op noted callback
+        // 5. AppOpsService is expected to drop the callback (via RemoteCallbackList) since
+        //    AppOpsUserService is frozen
+        // 6. freezeAndNoteSyncOp unfreezes AppOpsUserService
+        // 7. AppOpsLoggingTest calls AppOpsUserService.assertEmptyAsyncNoted
+        rethrowThrowableFrom {
+            testService.callFreezeAndNoteSyncOp(AppOpsUserClient(context))
+            freezingTestCompletion.get()
+            testService.assertEmptyAsyncNoted()
+        }
+    }
+
     @After
     fun removeNotedAppOpsCollector() {
         appOpsManager.setOnOpNotedCallback(null, null)
@@ -263,6 +296,20 @@
         context.unbindService(serviceConnection)
     }
 
+    fun <T> waitForState(queue: LinkedBlockingQueue<T>, state: T, duration: Duration): T? {
+        val timeSource = TimeSource.Monotonic
+        val start = timeSource.markNow()
+        var remaining = duration
+        while (remaining.inWholeMilliseconds > 0) {
+            val v = queue.poll(remaining.inWholeMilliseconds, TimeUnit.MILLISECONDS)
+            if (v == state) {
+                return v
+            }
+            remaining -= timeSource.markNow() - start
+        }
+        return null
+    }
+
     private inner class AppOpsUserClient(
         context: Context
     ) : IAppOpsUserClient.Stub() {
@@ -285,6 +332,31 @@
             nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), Binder.getCallingUid(), TEST_SERVICE_PKG)
         }
 
+        override fun freezeAndNoteSyncOp() {
+            handler.post {
+                var stateChanges = LinkedBlockingQueue<Int>()
+                // Leave some time for any pending binder transactions to complete.
+                //
+                // TODO(327047060) Remove this sleep and instead make am freeze wait for binder
+                // transactions to complete
+                Thread.sleep(1000)
+                testService.asBinder().addFrozenStateChangeCallback {
+                    _, state -> stateChanges.put(state)
+                }
+                InstrumentationRegistry.getInstrumentation().uiAutomation
+                    .executeShellCommand("am freeze $TEST_SERVICE_PKG")
+                waitForState(stateChanges, IBinder.FrozenStateChangeCallback.STATE_FROZEN,
+                    1000.milliseconds)
+                nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), Binder.getCallingUid(),
+                    TEST_SERVICE_PKG)
+                InstrumentationRegistry.getInstrumentation().uiAutomation
+                    .executeShellCommand("am unfreeze $TEST_SERVICE_PKG")
+                waitForState(stateChanges, IBinder.FrozenStateChangeCallback.STATE_UNFROZEN,
+                    1000.milliseconds)
+                freezingTestCompletion.complete(Unit)
+            }
+        }
+
         override fun noteSyncOpOtherUidNative() {
             nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), myUid, myPackage)
         }
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 65153f5..c2d8f91 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -52,11 +52,7 @@
  *  atest FrameworksCoreTests:PropertyInvalidatedCacheTests
  */
 @SmallTest
-@IgnoreUnderRavenwood(blockedBy = PropertyInvalidatedCache.class)
 public class PropertyInvalidatedCacheTests {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
     public final CheckFlagsRule mCheckFlagsRule =
             DeviceFlagsValueProvider.createCheckFlagsRule();
 
@@ -389,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 {
@@ -403,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
@@ -420,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.
         }
@@ -441,6 +448,8 @@
     // 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();
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
index f9b481f..d4618d7 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
+++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
@@ -71,6 +71,7 @@
     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";
@@ -166,6 +167,30 @@
 
     @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
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/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 483ebc2..fb1efa8 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -891,6 +891,65 @@
         });
     }
 
+    @Test
+    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
+    })
+    public void testTouchBoostReset() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
+        mActivityRule.runOnUiThread(() -> {
+            ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
+            layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+            layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
+            mMovingView.setLayoutParams(layoutParams);
+            mMovingView.setOnClickListener((v) -> {});
+        });
+        waitForFrameRateCategoryToSettle();
+
+        int[] position = new int[2];
+        mActivityRule.runOnUiThread(() -> {
+            mMovingView.getLocationOnScreen(position);
+            position[0] += mMovingView.getWidth() / 2;
+            position[1] += mMovingView.getHeight() / 2;
+        });
+        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+        long now = SystemClock.uptimeMillis();
+        MotionEvent down = MotionEvent.obtain(
+                now, // downTime
+                now, // eventTime
+                MotionEvent.ACTION_DOWN, // action
+                position[0], // x
+                position[1], // y
+                0 // metaState
+        );
+        down.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        instrumentation.sendPointerSync(down);
+        assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT, mViewRoot.getLastPreferredFrameRateCategory());
+
+        MotionEvent up = MotionEvent.obtain(
+                now, // downTime
+                now, // eventTime
+                MotionEvent.ACTION_UP, // action
+                position[0], // x
+                position[1], // y
+                0 // metaState
+        );
+        up.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        instrumentation.sendPointerSync(up);
+
+        // Wait for idle timeout - 100 ms logner to avoid flaky
+        Thread.sleep(3100);
+
+        // Should not touch boost after the time out
+        assertEquals(false, mViewRoot.getIsTouchBoosting());
+        assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+                mViewRoot.getLastPreferredFrameRateCategory());
+    }
+
+
     @LargeTest
     @Test
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 930e03d..3b0eab4 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 = 46;
+    private static final int NUM_MARSHALLED_PROPERTIES = 47;
 
     /**
      * The number of properties that are purposely not marshalled
@@ -58,7 +58,7 @@
 
     // The number of flags held in boolean properties. Their values should also be double-checked
     // in the methods above.
-    private static final int NUM_BOOLEAN_PROPERTIES = 27;
+    private static final int NUM_BOOLEAN_PROPERTIES = 28;
 
     @Test
     public void testStandardActions_serializationFlagIsValid() {
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 60b5a42..e7a6cb7 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -67,6 +67,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
+import java.util.Arrays;
 import java.util.concurrent.TimeUnit;
 
 @SmallTest
@@ -690,10 +691,9 @@
             FrameTracker tracker, long durationMillis, long vsyncId, @JankType int jankType) {
         final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
         doNothing().when(tracker).postCallback(captor.capture());
-        mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
-                new JankData(vsyncId, jankType, FRAME_TIME_60Hz, FRAME_TIME_60Hz,
-                TimeUnit.MILLISECONDS.toNanos(durationMillis))
-        });
+        mListenerCapture.getValue().onJankDataAvailable(Arrays.asList(new JankData(
+                vsyncId, jankType, FRAME_TIME_60Hz, FRAME_TIME_60Hz,
+                TimeUnit.MILLISECONDS.toNanos(durationMillis))));
         captor.getValue().run();
     }
 }
diff --git a/core/tests/packagemanagertests/Android.bp b/core/tests/packagemanagertests/Android.bp
index 8ff4998..c2713ad 100644
--- a/core/tests/packagemanagertests/Android.bp
+++ b/core/tests/packagemanagertests/Android.bp
@@ -16,6 +16,7 @@
         "androidx.test.rules",
         "frameworks-base-testutils",
         "mockito-target-minus-junit4",
+        "platform-test-annotations",
     ],
     libs: ["android.test.runner.stubs.system"],
     platform_apis: true,
diff --git a/core/tests/packagemanagertests/src/com/android/internal/pm/pkg/component/ParsedMainComponentUtilsTest.java b/core/tests/packagemanagertests/src/com/android/internal/pm/pkg/component/ParsedMainComponentUtilsTest.java
new file mode 100644
index 0000000..23bcb71
--- /dev/null
+++ b/core/tests/packagemanagertests/src/com/android/internal/pm/pkg/component/ParsedMainComponentUtilsTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.pm.pkg.component;
+
+import static android.security.Flags.FLAG_ENABLE_INTENT_MATCHING_FLAGS;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.RequiresFlagsEnabled;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class ParsedMainComponentUtilsTest {
+
+    private final Map<String, Integer> mStringToFlagMap = new HashMap<>();
+
+    {
+        mStringToFlagMap.put("none", ParsedMainComponentImpl.INTENT_MATCHING_FLAGS_NONE);
+        mStringToFlagMap.put("enforceIntentFilter",
+                ParsedMainComponentImpl.INTENT_MATCHING_FLAGS_ENFORCE_INTENT_FILTER);
+        mStringToFlagMap.put("allowNullAction",
+                ParsedMainComponentImpl.INTENT_MATCHING_FLAGS_ALLOW_NULL_ACTION);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_ENABLE_INTENT_MATCHING_FLAGS)
+    public void testResolveIntentMatchingFlags() {
+        assertResolution("", "", "");
+        assertResolution("none", "none", "none");
+
+        assertResolution("none", "enforceIntentFilter", "enforceIntentFilter");
+        assertResolution("enforceIntentFilter", "none", "none");
+        assertResolution("enforceIntentFilter|allowNullAction", "none",
+                "none");
+        assertResolution("enforceIntentFilter|allowNullAction", "enforceIntentFilter",
+                "enforceIntentFilter");
+
+        assertResolution("none", "", "none");
+        assertResolution("enforceIntentFilter", "", "enforceIntentFilter");
+        assertResolution("enforceIntentFilter|allowNullAction", "",
+                "enforceIntentFilter|allowNullAction");
+
+        assertResolution("", "none", "none");
+        assertResolution("", "enforceIntentFilter", "enforceIntentFilter");
+        assertResolution("", "enforceIntentFilter|allowNullAction",
+                "enforceIntentFilter|allowNullAction");
+    }
+
+    private void assertResolution(String applicationStringFlags, String componentStringFlags,
+            String expectedStringFlags) {
+        int applicationFlag = stringToFlag(applicationStringFlags);
+        int componentFlag = stringToFlag(componentStringFlags);
+
+        int expectedFlag = stringToFlag(expectedStringFlags);
+        int resolvedFlag = ParsedMainComponentUtils.resolveIntentMatchingFlags(applicationFlag,
+                componentFlag);
+
+        assertEquals(expectedFlag, resolvedFlag);
+    }
+
+    private int stringToFlag(String flags) {
+        int result = 0;
+        String[] flagList = flags.split("\\|");
+        for (String flag : flagList) {
+            String trimmedFlag = flag.trim();
+            result |= mStringToFlagMap.getOrDefault(trimmedFlag, 0);
+        }
+        return result;
+    }
+}
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index 8a250bd..1cd1190 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -403,6 +403,17 @@
 
     @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.
@@ -414,6 +425,15 @@
                 .build();
 
         assertNull(effect.computeCreateWaveformOffOnTimingsOrNull());
+
+        effect = VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 60)
+                .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
@@ -526,6 +546,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);
@@ -621,6 +652,38 @@
                 .build()
                 .validate();
 
+        VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30)
+                .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,
@@ -647,6 +710,34 @@
                                 /*timeMillis=*/ 0)
                         .build()
                         .validate());
+
+        assertThrows(IllegalStateException.class,
+                () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+                        .build().validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+                        .addControlPoint(/*amplitude=*/ -1.0f, /*frequencyHz=*/ 60f,
+                                /*timeMillis=*/ 20)
+                        .build()
+                        .validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+                        .addControlPoint(/*amplitude=*/ 1.1f, /*frequencyHz=*/ 60f,
+                                /*timeMillis=*/ 20)
+                        .build()
+                        .validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+                        .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 0f,
+                                /*timeMillis=*/ 20)
+                        .build()
+                        .validate());
+        assertThrows(IllegalArgumentException.class,
+                () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30)
+                        .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f,
+                                /*timeMillis=*/ 0)
+                        .build()
+                        .validate());
     }
 
     @Test
@@ -725,6 +816,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.
@@ -787,6 +893,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
@@ -1245,6 +1376,11 @@
                 .addTransition(Duration.ofMillis(500), targetAmplitude(0))
                 .build()
                 .isHapticFeedbackCandidate());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testIsHapticFeedbackCandidate_longEnvelopeEffects_notCandidates() {
         assertFalse(VibrationEffect.startWaveformEnvelope()
                 .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 200)
                 .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500)
@@ -1252,6 +1388,13 @@
                 .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400)
                 .build()
                 .isHapticFeedbackCandidate());
+        assertFalse(VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 40)
+                .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
@@ -1265,12 +1408,23 @@
                 .addTransition(Duration.ofMillis(300), targetAmplitude(0))
                 .build()
                 .isHapticFeedbackCandidate());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testIsHapticFeedbackCandidate_shortEnvelopeEffects_areCandidates() {
         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());
+        assertTrue(VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30)
+                .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/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 68d8ebb..b7a1c13 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -18,6 +18,8 @@
 
 import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
 import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
+import static com.android.text.flags.Flags.FLAG_DEPRECATE_ELEGANT_TEXT_HEIGHT_API;
+
 
 import android.annotation.ColorInt;
 import android.annotation.ColorLong;
@@ -39,6 +41,7 @@
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.text.flags.Flags;
@@ -61,6 +64,7 @@
  * geometries, text and bitmaps.
  */
 public class Paint {
+    private static final String TAG = "Paint";
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private long mNativePaint;
@@ -1803,8 +1807,18 @@
     /**
      * Get the elegant metrics flag.
      *
+     * Note:
+     * For applications target API 35 or later, this function returns true by default.
+     * For applications target API 36 or later, the function call will be ignored and the elegant
+     * text height is always enabled.
+     *
      * @return true if elegant metrics are enabled for text drawing.
+     * @deprecated The underlying UI fonts are deprecated and will be removed from the system image.
+     * Applications supporting scripts with large vertical metrics should adapt their UI by using
+     * fonts designed with corresponding vertical metrics.
      */
+    @Deprecated
+    @FlaggedApi(FLAG_DEPRECATE_ELEGANT_TEXT_HEIGHT_API)
     public boolean isElegantTextHeight() {
         return nGetElegantTextHeight(mNativePaint) != ELEGANT_TEXT_HEIGHT_DISABLED;
     }
@@ -1819,9 +1833,28 @@
      * variants that have not been compacted to fit Latin-based vertical
      * metrics, and also increases top and bottom bounds to provide more space.
      *
+     * <p>
+     * Note:
+     * For applications target API 35 or later, the default value will be true by default.
+     * For applications target API 36 or later, the function call will be ignored and the elegant
+     * text height is always enabled.
+     *
      * @param elegant set the paint's elegant metrics flag for drawing text.
+     * @deprecated This API will be no-op at some point in the future. The underlying UI fonts is
+     * deprecated and will be removed from the system image. Applications supporting scripts with
+     * large vertical metrics should adapt their UI by using fonts designed with corresponding
+     * vertical metrics.
      */
+    @Deprecated
+    @FlaggedApi(FLAG_DEPRECATE_ELEGANT_TEXT_HEIGHT_API)
     public void setElegantTextHeight(boolean elegant) {
+        if (Flags.deprecateElegantTextHeightApi() && !elegant
+                && CompatChanges.isChangeEnabled(DEPRECATE_UI_FONT_ENFORCE)) {
+            if (!elegant) {
+                Log.w(TAG, "The elegant text height cannot be turned off.");
+            }
+            return;
+        }
         nSetElegantTextHeight(mNativePaint,
                 elegant ? ELEGANT_TEXT_HEIGHT_ENABLED : ELEGANT_TEXT_HEIGHT_DISABLED);
     }
@@ -1839,6 +1872,19 @@
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     public static final long DEPRECATE_UI_FONT = 279646685L;
 
+    /**
+     * A change ID for deprecating UI fonts enforced.
+     *
+     * From API 36, the elegant text height will not be able to be overridden and always true if the
+     * app has a target SDK of API 36 or later.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = 36)
+    public static final long DEPRECATE_UI_FONT_ENFORCE = 349519475L;
+
+
     private void resetElegantTextHeight() {
         if (CompatChanges.isChangeEnabled(DEPRECATE_UI_FONT)) {
             nSetElegantTextHeight(mNativePaint, ELEGANT_TEXT_HEIGHT_UNSET);
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 211f74a..03a8b30 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -279,7 +279,8 @@
          * @hide
          */
         default void positionChanged(long frameNumber, int left, int top, int right, int bottom,
-                int clipLeft, int clipTop, int clipRight, int clipBottom) {
+                int clipLeft, int clipTop, int clipRight, int clipBottom,
+                int nodeWidth, int nodeHeight) {
             positionChanged(frameNumber, left, top, right, bottom);
         }
 
@@ -304,11 +305,12 @@
          * @hide */
         static boolean callPositionChanged2(WeakReference<PositionUpdateListener> weakListener,
                 long frameNumber, int left, int top, int right, int bottom,
-                int clipLeft, int clipTop, int clipRight, int clipBottom) {
+                int clipLeft, int clipTop, int clipRight, int clipBottom,
+                int nodeWidth, int nodeHeight) {
             final PositionUpdateListener listener = weakListener.get();
             if (listener != null) {
                 listener.positionChanged(frameNumber, left, top, right, bottom, clipLeft,
-                        clipTop, clipRight, clipBottom);
+                        clipTop, clipRight, clipBottom, nodeWidth, nodeHeight);
                 return true;
             } else {
                 return false;
@@ -401,10 +403,11 @@
 
         @Override
         public void positionChanged(long frameNumber, int left, int top, int right, int bottom,
-                int clipLeft, int clipTop, int clipRight, int clipBottom) {
+                int clipLeft, int clipTop, int clipRight, int clipBottom,
+                int nodeWidth, int nodeHeight) {
             for (PositionUpdateListener pul : mListeners) {
                 pul.positionChanged(frameNumber, left, top, right, bottom, clipLeft, clipTop,
-                        clipRight, clipBottom);
+                        clipRight, clipBottom, nodeWidth, nodeHeight);
             }
         }
 
diff --git a/graphics/java/android/graphics/RuntimeColorFilter.java b/graphics/java/android/graphics/RuntimeColorFilter.java
new file mode 100644
index 0000000..52724ce
--- /dev/null
+++ b/graphics/java/android/graphics/RuntimeColorFilter.java
@@ -0,0 +1,305 @@
+/*
+ * 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.graphics;
+
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+
+import com.android.graphics.hwui.flags.Flags;
+
+
+/**
+ * <p>A {@link RuntimeColorFilter} calculates a per-pixel color based on the output of a user
+ *  * defined Android Graphics Shading Language (AGSL) function.</p>
+ *
+ * <p>This AGSL function takes in an input color to be operated on. This color is in sRGB and the
+ *  * output is also interpreted as sRGB. The AGSL function signature expects a single input
+ *  * of color (packed as a half4 or float4 or vec4).</p>
+ *
+ * <pre class="prettyprint">
+ * vec4 main(half4 in_color);
+ * </pre>
+ */
+@FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+public class RuntimeColorFilter extends ColorFilter {
+
+    private String mAgsl;
+
+    /**
+     * Creates a new RuntimeColorFilter.
+     *
+     * @param agsl The text of AGSL color filter program to run.
+     */
+    public RuntimeColorFilter(@NonNull String agsl) {
+        if (agsl == null) {
+            throw new NullPointerException("RuntimeColorFilter requires a non-null AGSL string");
+        }
+        mAgsl = agsl;
+        // call to parent class to register native RuntimeColorFilter
+        // TODO: find way to get super class to create native instance without requiring the storage
+        // of agsl string
+        getNativeInstance();
+
+    }
+    /**
+     * Sets the uniform color value corresponding to this color filter.  If the effect does not have
+     * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4
+     * and corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the color uniform declared in the AGSL program
+     * @param color the provided sRGB color
+     */
+    public void setColorUniform(@NonNull String uniformName, @ColorInt int color) {
+        setUniform(uniformName, Color.valueOf(color).getComponents(), true);
+    }
+
+    /**
+     * Sets the uniform color value corresponding to this color filter.  If the effect does not have
+     * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4
+     * and corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the color uniform declared in the AGSL program
+     * @param color the provided sRGB color
+     */
+    public void setColorUniform(@NonNull String uniformName, @ColorLong long color) {
+        Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+        setUniform(uniformName, exSRGB.getComponents(), true);
+    }
+
+    /**
+     * Sets the uniform color value corresponding to this color filter.  If the effect does not have
+     * a uniform with that name or if the uniform is declared with a type other than vec3 or vec4
+     * and corresponding layout(color) annotation then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the color uniform declared in the AGSL program
+     * @param color the provided sRGB color
+     */
+    public void setColorUniform(@NonNull String uniformName, @NonNull Color color) {
+        if (color == null) {
+            throw new NullPointerException("The color parameter must not be null");
+        }
+        Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
+        setUniform(uniformName, exSRGB.getComponents(), true);
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than a float or
+     * float[1] then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setFloatUniform(@NonNull String uniformName, float value) {
+        setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1);
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than a vec2 or
+     * float[2] then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setFloatUniform(@NonNull String uniformName, float value1, float value2) {
+        setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2);
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than a vec3 or
+     * float[3] then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+            float value3) {
+        setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3);
+
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than a vec4 or
+     * float[4] then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+            float value3, float value4) {
+        setFloatUniform(uniformName, value1, value2, value3, value4, 4);
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than a float
+     * (for N=1), vecN, or float[N] where N is the length of the values param then an
+     * IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) {
+        setUniform(uniformName, values, false);
+    }
+
+    private void setFloatUniform(@NonNull String uniformName, float value1, float value2,
+            float value3, float value4, int count) {
+        if (uniformName == null) {
+            throw new NullPointerException("The uniformName parameter must not be null");
+        }
+        nativeUpdateUniforms(getNativeInstance(), uniformName, value1, value2, value3, value4,
+                count);
+    }
+
+    private void setUniform(@NonNull String uniformName, @NonNull float[] values, boolean isColor) {
+        if (uniformName == null) {
+            throw new NullPointerException("The uniformName parameter must not be null");
+        }
+        if (values == null) {
+            throw new NullPointerException("The uniform values parameter must not be null");
+        }
+        nativeUpdateUniforms(getNativeInstance(), uniformName, values, isColor);
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than an int or int[1]
+     * then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setIntUniform(@NonNull String uniformName, int value) {
+        setIntUniform(uniformName, value, 0, 0, 0, 1);
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than an ivec2 or
+     * int[2] then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setIntUniform(@NonNull String uniformName, int value1, int value2) {
+        setIntUniform(uniformName, value1, value2, 0, 0, 2);
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than an ivec3 or
+     * int[3] then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) {
+        setIntUniform(uniformName, value1, value2, value3, 0, 3);
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than an ivec4 or
+     * int[4] then an IllegalArgumentException is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setIntUniform(@NonNull String uniformName, int value1, int value2,
+            int value3, int value4) {
+        setIntUniform(uniformName, value1, value2, value3, value4, 4);
+    }
+
+    /**
+     * Sets the uniform value corresponding to this color filter.  If the effect does not have a
+     * uniform with that name or if the uniform is declared with a type other than an int (for N=1),
+     * ivecN, or int[N] where N is the length of the values param then an IllegalArgumentException
+     * is thrown.
+     *
+     * @param uniformName name matching the uniform declared in the AGSL program
+     */
+    public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) {
+        if (uniformName == null) {
+            throw new NullPointerException("The uniformName parameter must not be null");
+        }
+        if (values == null) {
+            throw new NullPointerException("The uniform values parameter must not be null");
+        }
+        nativeUpdateUniforms(getNativeInstance(), uniformName, values);
+    }
+
+    private void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3,
+            int value4, int count) {
+        if (uniformName == null) {
+            throw new NullPointerException("The uniformName parameter must not be null");
+        }
+        nativeUpdateUniforms(getNativeInstance(), uniformName, value1, value2, value3, value4,
+                count);
+    }
+
+    /**
+     * Assigns the uniform shader to the provided shader parameter.  If the shader program does not
+     * have a uniform shader with that name then an IllegalArgumentException is thrown.
+     *
+     * @param shaderName name matching the uniform declared in the AGSL program
+     * @param shader shader passed into the AGSL program for sampling
+     */
+    public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
+        if (shaderName == null) {
+            throw new NullPointerException("The shaderName parameter must not be null");
+        }
+        if (shader == null) {
+            throw new NullPointerException("The shader parameter must not be null");
+        }
+        nativeUpdateChild(getNativeInstance(), shaderName, shader.getNativeInstance());
+    }
+
+    /**
+     * Assigns the uniform color filter to the provided color filter parameter.  If the shader
+     * program does not have a uniform color filter with that name then an IllegalArgumentException
+     * is thrown.
+     *
+     * @param filterName name matching the uniform declared in the AGSL program
+     * @param colorFilter filter passed into the AGSL program for sampling
+     */
+    public void setInputColorFilter(@NonNull String filterName, @NonNull ColorFilter colorFilter) {
+        if (filterName == null) {
+            throw new NullPointerException("The filterName parameter must not be null");
+        }
+        if (colorFilter == null) {
+            throw new NullPointerException("The colorFilter parameter must not be null");
+        }
+        nativeUpdateChild(getNativeInstance(), filterName, colorFilter.getNativeInstance());
+    }
+
+    /** @hide */
+    @Override
+    protected long createNativeInstance() {
+        return nativeCreateRuntimeColorFilter(mAgsl);
+    }
+
+    private static native long nativeCreateRuntimeColorFilter(String agsl);
+    private static native void nativeUpdateUniforms(
+            long colorFilter, String uniformName, float[] uniforms, boolean isColor);
+    private static native void nativeUpdateUniforms(
+            long colorFilter, String uniformName, float value1, float value2, float value3,
+            float value4, int count);
+    private static native void nativeUpdateUniforms(
+            long colorFilter, String uniformName, int[] uniforms);
+    private static native void nativeUpdateUniforms(
+            long colorFilter, String uniformName, int value1, int value2, int value3,
+            int value4, int count);
+    private static native void nativeUpdateChild(long colorFilter, String childName, long child);
+
+}
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 82b3f68..71baed8 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -37,6 +37,7 @@
 import android.util.TypedValue;
 import android.view.View;
 
+import com.android.graphics.hwui.flags.Flags;
 import com.android.internal.R;
 
 import dalvik.annotation.optimization.FastNative;
@@ -525,6 +526,35 @@
         }
     }
 
+    @Override
+    public void setFilterBitmap(boolean filterBitmap) {
+        if (!Flags.animatedImageDrawableFilterBitmap()) {
+            super.setFilterBitmap(filterBitmap);
+            return;
+        }
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException(
+              "called setFilterBitmap on empty AnimatedImageDrawable"
+            );
+        }
+        if (nSetFilterBitmap(mState.mNativePtr, filterBitmap)) {
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public boolean isFilterBitmap() {
+        if (!Flags.animatedImageDrawableFilterBitmap()) {
+            return super.isFilterBitmap();
+        }
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException(
+                "called isFilterBitmap on empty AnimatedImageDrawable"
+            );
+        }
+        return nGetFilterBitmap(mState.mNativePtr);
+    }
+
     private void postOnAnimationStart() {
         if (mAnimationCallbacks == null) {
             return;
@@ -618,4 +648,8 @@
     private static native void nSetMirrored(long nativePtr, boolean mirror);
     @FastNative
     private static native void nSetBounds(long nativePtr, Rect rect);
+    @FastNative
+    private static native boolean nSetFilterBitmap(long nativePtr, boolean filterBitmap);
+    @FastNative
+    private static native boolean nGetFilterBitmap(long nativePtr);
 }
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index 792e248..ed17fde 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -24,7 +24,6 @@
 import android.graphics.Paint;
 import android.graphics.Typeface;
 import android.graphics.fonts.Font;
-import android.os.Build;
 
 import com.android.internal.util.Preconditions;
 import com.android.text.flags.Flags;
@@ -54,8 +53,6 @@
                         Typeface.class.getClassLoader(), nReleaseFunc());
     }
 
-    private static boolean sIsRobolectric = Build.FINGERPRINT.equals("robolectric");
-
     private final long mLayoutPtr;
     private final float mXOffset;
     private final float mYOffset;
@@ -255,7 +252,7 @@
         mXOffset = xOffset;
         mYOffset = yOffset;
 
-        if (!sIsRobolectric && Flags.typefaceRedesign()) {
+        if (Flags.typefaceRedesign()) {
             int fontCount = nGetFontCount(layoutPtr);
             mFonts = new ArrayList<>(fontCount);
             for (int i = 0; i < fontCount; ++i) {
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/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index ad194f7..6398c7a2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -39,6 +39,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.ColorInt;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityThread;
@@ -1394,10 +1395,14 @@
         }
 
         private void showVeils(@NonNull SurfaceControl.Transaction t) {
-            final Color primaryVeilColor = getContainerBackgroundColor(
-                    mProperties.mPrimaryContainer, DEFAULT_PRIMARY_VEIL_COLOR);
-            final Color secondaryVeilColor = getContainerBackgroundColor(
-                    mProperties.mSecondaryContainer, DEFAULT_SECONDARY_VEIL_COLOR);
+            final Color primaryVeilColor = getVeilColor(
+                    mProperties.mDividerAttributes.getPrimaryVeilColor(),
+                    mProperties.mPrimaryContainer,
+                    DEFAULT_PRIMARY_VEIL_COLOR);
+            final Color secondaryVeilColor = getVeilColor(
+                    mProperties.mDividerAttributes.getSecondaryVeilColor(),
+                    mProperties.mSecondaryContainer,
+                    DEFAULT_SECONDARY_VEIL_COLOR);
             t.setColor(mPrimaryVeil, colorToFloatArray(primaryVeilColor))
                     .setColor(mSecondaryVeil, colorToFloatArray(secondaryVeilColor))
                     .setLayer(mDividerSurface, DIVIDER_LAYER)
@@ -1444,6 +1449,21 @@
             }
         }
 
+        /**
+         * Returns the veil color.
+         *
+         * If the configured color is not transparent, we use the configured color, otherwise we use
+         * the window background color of the top activity. If the background color of the top
+         * activity is unavailable, the default color is used.
+         */
+        @NonNull
+        private static Color getVeilColor(@ColorInt int configuredColor,
+                @NonNull TaskFragmentContainer container, @NonNull Color defaultColor) {
+            return configuredColor != Color.TRANSPARENT
+                    ? Color.valueOf(configuredColor)
+                    : getContainerBackgroundColor(container, defaultColor);
+        }
+
         private static float[] colorToFloatArray(@NonNull Color color) {
             return new float[]{color.red(), color.green(), color.blue()};
         }
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/multivalentScreenshotTests/Android.bp b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
index 61c09f2..7f54c75 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
@@ -22,42 +22,6 @@
     default_team: "trendy_team_multitasking_windowing",
 }
 
-android_app {
-    name: "WMShellRobolectricScreenshotTestApp",
-    platform_apis: true,
-    certificate: "platform",
-    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",
-    use_resource_processor: true,
-}
-
-android_robolectric_test {
-    name: "WMShellRobolectricScreenshotTests",
-    instrumentation_for: "WMShellRobolectricScreenshotTestApp",
-    upstream: true,
-    java_resource_dirs: [
-        "robolectric/config",
-    ],
-    srcs: [
-        "src/**/*.kt",
-    ],
-    static_libs: [
-        "junit",
-        "androidx.test.runner",
-        "androidx.test.rules",
-        "androidx.test.ext.junit",
-        "truth",
-        "platform-parametric-runner-lib",
-    ],
-    auto_gen_config: true,
-}
-
 android_test {
     name: "WMShellMultivalentScreenshotTestsOnDevice",
     srcs: [
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 5b429c0..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 6028fa2..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 a163d92..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 25d2e34c..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/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 52ce8cb..0b515f5 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -23,14 +23,15 @@
 import android.graphics.Color
 import android.graphics.drawable.Icon
 import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
-import android.view.IWindowManager
 import android.view.WindowManager
-import android.view.WindowManagerGlobal
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.protolog.ProtoLog
 import com.android.launcher3.icons.BubbleIconFactory
@@ -41,6 +42,7 @@
 import com.android.wm.shell.common.FloatingContentCoordinator
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
 import com.android.wm.shell.taskview.TaskView
 import com.android.wm.shell.taskview.TaskViewTaskController
 import com.google.common.truth.Truth.assertThat
@@ -51,9 +53,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import org.mockito.kotlin.verify
 import java.util.concurrent.Semaphore
 import java.util.concurrent.TimeUnit
 import java.util.function.Consumer
@@ -72,7 +72,7 @@
     private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
     private lateinit var bubbleStackView: BubbleStackView
     private lateinit var shellExecutor: ShellExecutor
-    private lateinit var windowManager: IWindowManager
+    private lateinit var windowManager: WindowManager
     private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
     private lateinit var bubbleData: BubbleData
     private lateinit var bubbleStackViewManager: FakeBubbleStackViewManager
@@ -83,9 +83,8 @@
         PhysicsAnimatorTestUtils.prepareForTest()
         // Disable protolog tool when running the tests from studio
         ProtoLog.REQUIRE_PROTOLOGTOOL = false
-        windowManager = WindowManagerGlobal.getWindowManagerService()!!
         shellExecutor = TestShellExecutor()
-        val windowManager = context.getSystemService(WindowManager::class.java)
+        windowManager = context.getSystemService(WindowManager::class.java)
         iconFactory =
             BubbleIconFactory(
                 context,
@@ -354,6 +353,16 @@
         assertThat(bubbleStackView.getBubbleIndex(bubbleOverflow)).isGreaterThan(-1)
     }
 
+    @Test
+    fun removeFromWindow_stopMonitoringSwipeUpGesture() {
+        spyOn(bubbleStackView)
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            // No way to add to window in the test environment right now so just pretend
+            bubbleStackView.onDetachedFromWindow()
+        }
+        verify(bubbleStackView).stopMonitoringSwipeUpGesture()
+    }
+
     private fun createAndInflateChatBubble(key: String): Bubble {
         val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
         val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
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/drawable/desktop_windowing_education_promo_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_promo_background.xml
new file mode 100644
index 0000000..645d24d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_education_promo_background.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 android:shape="rectangle"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainerLow" />
+    <corners android:radius="@dimen/desktop_windowing_education_promo_corner_radius" />
+</shape>
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/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
index 62782a7..e7ead63 100644
--- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml
@@ -22,14 +22,14 @@
     android:gravity="bottom|end">
 
     <include android:id="@+id/size_compat_hint"
-        android:visibility="gone"
+        android:visibility="invisible"
         android:layout_width="@dimen/compat_hint_width"
         android:layout_height="wrap_content"
         layout="@layout/compat_mode_hint"/>
 
     <ImageButton
         android:id="@+id/size_compat_restart_button"
-        android:visibility="gone"
+        android:visibility="invisible"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/compat_button_margin"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_windowing_education_promo.xml b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_promo.xml
new file mode 100644
index 0000000..eebfd4b
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_windowing_education_promo.xml
@@ -0,0 +1,36 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/education_container"
+    android:layout_width="@dimen/desktop_windowing_education_promo_width"
+    android:layout_height="@dimen/desktop_windowing_education_promo_height"
+    android:elevation="1dp"
+    android:orientation="vertical"
+    android:paddingHorizontal="32dp"
+    android:paddingVertical="24dp"
+    android:background="@drawable/desktop_windowing_education_promo_background">
+    <TextView
+        android:id="@+id/education_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="24sp"
+        android:lineHeight="32dp"
+        android:textFontWeight="400"
+        android:fontFamily="google-sans-text"
+        android:layout_gravity="center_horizontal"
+        android:gravity="center"/>
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml
index 433d854..b5f04c3 100644
--- a/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml
+++ b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml
@@ -22,14 +22,14 @@
     android:gravity="bottom|end">
 
     <include android:id="@+id/user_aspect_ratio_settings_hint"
-        android:visibility="gone"
+        android:visibility="invisible"
         android:layout_width="@dimen/compat_hint_width"
         android:layout_height="wrap_content"
         layout="@layout/compat_mode_hint"/>
 
     <ImageButton
         android:id="@+id/user_aspect_ratio_settings_button"
-        android:visibility="gone"
+        android:visibility="invisible"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/compat_button_margin"
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..a8febc8 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"مجسَّم"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"استعادة"</string>
     <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..8c924e3 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -133,9 +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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ইমাৰ্ছিভ"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"পুনঃস্থাপন কৰক"</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) -->
-    <skip />
+    <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..22a445f 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -133,9 +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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"ইমারসিভ"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ফিরিয়ে আনুন"</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) -->
-    <skip />
+    <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..73f30d7 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Interaktivno"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vrati"</string>
     <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..6a5780e 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Pohlcující"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Obnovit"</string>
     <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..d02fae2 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"Καθηλωτικό"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Επαναφορά"</string>
     <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..f991145 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <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">"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..f991145 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <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">"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..f991145 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <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">"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..527793e 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"इमर्सिव"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"वापस लाएं"</string>
     <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..659d1ec 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Interaktivno"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Vrati"</string>
     <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..ca1bc15 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Umlykjandi"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Endurheimta"</string>
     <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..87919b5 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Immersivo"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Ripristina"</string>
     <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..c7a77d9 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"没入モード"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"復元"</string>
     <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..39362ef 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"იმერსიული"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"აღდგენა"</string>
     <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..9c4ae05 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"ជក់ចិត្ត"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ស្ដារ"</string>
     <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..f365cfb 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"ಇಮ್ಮರ್ಸಿವ್"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"ಮರುಸ್ಥಾಪಿಸಿ"</string>
     <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..5a7f58e 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Įtraukiantis"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Atkurti"</string>
     <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..871bc3f 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"इमर्सिव्ह"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"रिस्टोअर करा"</string>
     <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..71666ca 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Mengasyikkan"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Pulihkan"</string>
     <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..7015b2c 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"इमर्सिभ"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"रिस्टोर गर्नुहोस्"</string>
     <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..5f78b13 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Tryb immersyjny"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Przywróć"</string>
     <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..cd78ef9 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Envolvente"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Restaurar"</string>
     <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..ae7e524 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <string name="desktop_mode_maximize_menu_immersive_button_text" msgid="559492223133829481">"Poglobljeno"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"Obnovi"</string>
     <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..2c73d3a 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"ஈடுபட வைக்கும்"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"மீட்டெடுக்கும்"</string>
     <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..b17d4d1 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"లీనమయ్యే"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"రీస్టోర్ చేయండి"</string>
     <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..43cee41 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"สมจริง"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"คืนค่า"</string>
     <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..4284995 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -133,6 +133,8 @@
     <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>
+    <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">"I-restore"</string>
     <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..2fb3f5a 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -133,6 +133,8 @@
     <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_immersive_button_text" msgid="559492223133829481">"沉浸式"</string>
+    <string name="desktop_mode_maximize_menu_immersive_restore_button_text" msgid="4900114367354709257">"恢复"</string>
     <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/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index a14461a..59e6c77 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -186,4 +186,6 @@
 
     <!-- Apps that can trigger Desktop Windowing App handle Education -->
     <string-array name="desktop_windowing_app_handle_education_allowlist_apps"></string-array>
+    <!-- Apps that can trigger Desktop Windowing App-To-Web Education -->
+    <string-array name="desktop_windowing_app_to_web_education_allowlist_apps"></string-array>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 6cd380d..34f950c 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. -->
@@ -650,4 +650,11 @@
     <dimen name="open_by_default_settings_dialog_radio_button_height">64dp</dimen>
     <!-- The width of radio buttons in the open by default settings dialog. -->
     <dimen name="open_by_default_settings_dialog_radio_button_width">316dp</dimen>
+
+    <!-- The width of the desktop windowing education promo. -->
+    <dimen name="desktop_windowing_education_promo_width">412dp</dimen>
+    <!-- The height of the desktop windowing education promo. -->
+    <dimen name="desktop_windowing_education_promo_height">352dp</dimen>
+    <!-- The corner radius of the desktop windowing education promo. -->
+    <dimen name="desktop_windowing_education_promo_corner_radius">28dp</dimen>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index ef0386a..52585d4 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -339,4 +339,7 @@
     <string name="open_by_default_dialog_in_browser_text">In your browser</string>
     <!-- Text for open by default settings dialog dismiss button. -->
     <string name="open_by_default_dialog_dismiss_button_text">OK</string>
+
+    <!-- Text for the App-to-Web education promo. -->
+    <string name="desktop_windowing_app_to_web_education_text">Quickly open apps in your browser here</string>
 </resources>
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 eb7ef14..62ca5c6 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
@@ -18,6 +18,7 @@
 
 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -170,26 +171,34 @@
 
         private final Context mContext;
         private final int mAppIconSizePx;
-        private final Rect mAppBounds;
+        /**
+         * The bounds of the application window relative to the task leash.
+         */
+        private final Rect mRelativeAppBounds;
         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) {
+        // TODO(b/356277166): add non-match_parent support on PIP2.
+        /**
+         * @param context the {@link Context} that contains the icon information
+         * @param relativeAppBounds the bounds of the app window frame relative to the task leash
+         * @param destinationBounds the bounds for rhe PIP task
+         * @param appIcon the app icon {@link Drawable}
+         * @param appIconSizePx the icon dimension in pixel
+         */
+        public PipAppIconOverlay(@NonNull Context context, @NonNull Rect relativeAppBounds,
+                @NonNull Rect destinationBounds, @NonNull 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);
+            final int overlaySize = getOverlaySize(relativeAppBounds, 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());
+            mRelativeAppBounds = relativeAppBounds;
 
             mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
             prepareAppIconOverlay(appIcon);
@@ -206,9 +215,9 @@
          * 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();
+        public static int getOverlaySize(Rect overlayBounds, Rect destinationBounds) {
+            final int appWidth = overlayBounds.width();
+            final int appHeight = overlayBounds.height();
 
             return Math.max(Math.max(appWidth, appHeight),
                     Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
@@ -230,15 +239,15 @@
             mTmpTransform.reset();
             // In order for the overlay to always cover the pip window, the overlay may have a
             // size larger than the pip window. Make sure that app icon is at the center.
-            final int appBoundsCenterX = mAppBounds.centerX();
-            final int appBoundsCenterY = mAppBounds.centerY();
+            final int appBoundsCenterX = mRelativeAppBounds.centerX();
+            final int appBoundsCenterY = mRelativeAppBounds.centerY();
             mTmpTransform.setTranslate(
                     appBoundsCenterX - mOverlayHalfSize,
                     appBoundsCenterY - mOverlayHalfSize);
             // Scale back the bitmap with the pivot point at center.
             final float scale = Math.min(
-                    (float) mAppBounds.width() / currentBounds.width(),
-                    (float) mAppBounds.height() / currentBounds.height());
+                    (float) mRelativeAppBounds.width() / currentBounds.width(),
+                    (float) mRelativeAppBounds.height() / currentBounds.height());
             mTmpTransform.postScale(scale, scale, appBoundsCenterX, appBoundsCenterY);
             atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
                     .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
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 5eb5d89..f296c71 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;
@@ -190,6 +191,16 @@
                 @Override
                 public void onResult(@Nullable Bundle result) {
                     mShellExecutor.execute(() -> {
+                        if (mBackGestureStarted && result != null && result.getBoolean(
+                                BackNavigationInfo.KEY_TOUCH_GESTURE_TRANSFERRED)) {
+                            // Host app won't able to process motion event anymore, so pilfer
+                            // pointers anyway.
+                            if (mBackNavigationInfo != null) {
+                                mBackNavigationInfo.disableAppProgressGenerationAllowed();
+                            }
+                            tryPilferPointers();
+                            return;
+                        }
                         if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
                             // If an uninterruptible animation is already in progress, we should
                             // ignore this due to it may cause focus lost. (alpha = 0)
@@ -1262,6 +1273,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");
@@ -1280,7 +1300,7 @@
             final TransitionInfo init = mOpenTransitionInfo;
             // Find prepare open target
             boolean openShowWallpaper = false;
-            final ArrayList<OpenChangeInfo> targets = new ArrayList<>();
+            final ArrayList<SurfaceControl> openSurfaces = new ArrayList<>();
             int tmpSize;
             for (int j = init.getChanges().size() - 1; j >= 0; --j) {
                 final TransitionInfo.Change change = init.getChanges().get(j);
@@ -1293,13 +1313,13 @@
                             && openToken == null) {
                         continue;
                     }
-                    targets.add(new OpenChangeInfo(openComponent, openTaskId, openToken));
+                    openSurfaces.add(change.getLeash());
                     if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
                         openShowWallpaper = true;
                     }
                 }
             }
-            if (targets.isEmpty()) {
+            if (openSurfaces.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);
@@ -1311,7 +1331,7 @@
             tmpSize = info.getChanges().size();
             for (int j = 0; j < tmpSize; ++j) {
                 final TransitionInfo.Change change = info.getChanges().get(j);
-                if (isOpenChangeMatched(targets, change)) {
+                if (isOpenSurfaceMatched(openSurfaces, change)) {
                     // This is original close target, potential be close, but cannot determine
                     // from it.
                     if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
@@ -1324,15 +1344,15 @@
             }
             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 (isOpenChangeMatched(targets, change)) {
+                    if (isOpenSurfaceMatched(openSurfaces, change)) {
                         if (TransitionUtil.isClosingMode(change.getMode())) {
                             excludeOpenTarget = true;
                         }
@@ -1353,7 +1373,7 @@
                         if (change.hasFlags(FLAG_IS_WALLPAPER)) {
                             continue;
                         }
-                        if (isOpenChangeMatched(targets, change)) {
+                        if (isOpenSurfaceMatched(openSurfaces, change)) {
                             if (excludeOpenTarget) {
                                 // App has triggered another change during predictive back
                                 // transition, filter out predictive back target.
@@ -1388,7 +1408,7 @@
                 if (nonBackClose && nonBackOpen) {
                     for (int j = info.getChanges().size() - 1; j >= 0; --j) {
                         final TransitionInfo.Change change = info.getChanges().get(j);
-                        if (isOpenChangeMatched(targets, change)) {
+                        if (isOpenSurfaceMatched(openSurfaces, change)) {
                             info.getChanges().remove(j);
                         } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
                             info.getChanges().remove(j);
@@ -1515,14 +1535,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);
@@ -1532,6 +1555,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,
@@ -1666,22 +1692,10 @@
         return INVALID_TASK_ID;
     }
 
-    private static boolean isSameChangeTarget(ComponentName topActivity, int taskId,
-            WindowContainerToken token, TransitionInfo.Change change) {
-        final ComponentName openChange = findComponentName(change);
-        final int firstTaskId = findTaskId(change);
-        final WindowContainerToken openToken = findToken(change);
-        return (openChange != null && openChange.equals(topActivity))
-                || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId)
-                || (openToken != null && openToken.equals(token));
-    }
-
-    static boolean isOpenChangeMatched(@NonNull ArrayList<OpenChangeInfo> targets,
+    static boolean isOpenSurfaceMatched(@NonNull ArrayList<SurfaceControl> openSurfaces,
             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)) {
+        for (int i = openSurfaces.size() - 1; i >= 0; --i) {
+            if (openSurfaces.get(i).isSameSurface(change.getLeash())) {
                 return true;
             }
         }
@@ -1745,16 +1759,4 @@
             }
         }
     }
-
-    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..5f0eed9 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 {
@@ -1212,7 +1212,7 @@
      */
     public void startBubbleDrag(String bubbleKey) {
         if (mBubbleData.getSelectedBubble() != null) {
-            mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ false);
+            collapseExpandedViewForBubbleBar();
         }
         if (mBubbleStateListener != null) {
             boolean overflow = BubbleOverflow.KEY.equals(bubbleKey);
@@ -1304,6 +1304,7 @@
         if (BubbleOverflow.KEY.equals(key)) {
             mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow());
             mLayerView.showExpandedView(mBubbleData.getOverflow());
+            mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
             return;
         }
 
@@ -1315,6 +1316,7 @@
             // already in the stack
             mBubbleData.setSelectedBubbleFromLauncher(b);
             mLayerView.showExpandedView(b);
+            mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_EXPANDED);
         } else if (mBubbleData.hasOverflowBubbleWithKey(b.getKey())) {
             // TODO: (b/271468319) handle overflow
         } else {
@@ -1759,6 +1761,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) {
@@ -2021,12 +2026,16 @@
         public void expansionChanged(boolean isExpanded) {
             // in bubble bar mode, let the request to show the expanded view come from launcher.
             // only collapse here if we're collapsing.
-            if (mLayerView != null && !isExpanded) {
-                if (mBubblePositioner.isImeVisible()) {
-                    // If we're collapsing, hide the IME
-                    hideCurrentInputMethod();
-                }
-                mLayerView.collapse();
+            if (!isExpanded) {
+                collapseExpandedViewForBubbleBar();
+            }
+
+            BubbleLogger.Event event = isExpanded ? BubbleLogger.Event.BUBBLE_BAR_EXPANDED
+                    : BubbleLogger.Event.BUBBLE_BAR_COLLAPSED;
+            if (mBubbleData.getSelectedBubble() instanceof Bubble bubble) {
+                mLogger.log(bubble, event);
+            } else {
+                mLogger.log(event);
             }
         }
 
@@ -2179,6 +2188,16 @@
         }
     }
 
+    private void collapseExpandedViewForBubbleBar() {
+        if (mLayerView != null && mLayerView.isExpanded()) {
+            if (mBubblePositioner.isImeVisible()) {
+                // If we're collapsing, hide the IME
+                hideCurrentInputMethod();
+            }
+            mLayerView.collapse();
+        }
+    }
+
     private void updateOverflowButtonDot() {
         BubbleOverflow overflow = mBubbleData.getOverflow();
         if (overflow == null) return;
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/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 35a0d07..88f55b8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1704,6 +1704,7 @@
         getViewTreeObserver().removeOnPreDrawListener(mViewUpdater);
         getViewTreeObserver().removeOnDrawListener(mSystemGestureExcludeUpdater);
         getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+        stopMonitoringSwipeUpGesture();
     }
 
     @Override
@@ -2313,7 +2314,8 @@
     /**
      * Stop monitoring for swipe up gesture
      */
-    void stopMonitoringSwipeUpGesture() {
+    @VisibleForTesting
+    public void stopMonitoringSwipeUpGesture() {
         stopMonitoringSwipeUpGestureInternal();
     }
 
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/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index 8592170..813772f 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
@@ -71,6 +71,11 @@
      */
     private static final int SNAP_MODE_MINIMIZED = 3;
 
+    /**
+     * A mode where apps can be "flexibly offscreen" on smaller displays.
+     */
+    private static final int SNAP_FLEXIBLE_SPLIT = 4;
+
     private final float mMinFlingVelocityPxPerSecond;
     private final float mMinDismissVelocityPxPerSecond;
     private final int mDisplayWidth;
@@ -78,6 +83,7 @@
     private final int mDividerSize;
     private final ArrayList<SnapTarget> mTargets = new ArrayList<>();
     private final Rect mInsets = new Rect();
+    private final Rect mPinnedTaskbarInsets = new Rect();
     private final int mSnapMode;
     private final boolean mFreeSnapMode;
     private final int mMinimalSizeResizableTask;
@@ -88,6 +94,8 @@
     /** Allows split ratios that go offscreen (a.k.a. "flexible split") */
     private final boolean mAllowOffscreenRatios;
     private final boolean mIsLeftRightSplit;
+    /** In SNAP_MODE_MINIMIZED, the side of the screen on which an app will "dock" when minimized */
+    private final int mDockSide;
 
     /** The first target which is still splitting the screen */
     private final SnapTarget mFirstSplitTarget;
@@ -101,14 +109,14 @@
 
 
     public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
-            boolean isLeftRightSplit, Rect insets, int dockSide) {
+            boolean isLeftRightSplit, Rect insets, Rect pinnedTaskbarInsets, int dockSide) {
         this(res, displayWidth, displayHeight, dividerSize, isLeftRightSplit, insets,
-                dockSide, false /* minimized */, true /* resizable */);
+                pinnedTaskbarInsets, dockSide, false /* minimized */, true /* resizable */);
     }
 
     public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
-            boolean isLeftRightSplit, Rect insets, int dockSide, boolean isMinimizedMode,
-            boolean isHomeResizable) {
+            boolean isLeftRightSplit, Rect insets, Rect pinnedTaskbarInsets, int dockSide,
+            boolean isMinimizedMode, boolean isHomeResizable) {
         mMinFlingVelocityPxPerSecond =
                 MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
         mMinDismissVelocityPxPerSecond =
@@ -117,9 +125,18 @@
         mDisplayWidth = displayWidth;
         mDisplayHeight = displayHeight;
         mIsLeftRightSplit = isLeftRightSplit;
+        mDockSide = dockSide;
         mInsets.set(insets);
-        mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED :
-                res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode);
+        mPinnedTaskbarInsets.set(pinnedTaskbarInsets);
+        if (Flags.enableFlexibleTwoAppSplit()) {
+            mSnapMode = SNAP_FLEXIBLE_SPLIT;
+        } 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,11 +146,10 @@
         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);
+        calculateTargets();
         mFirstSplitTarget = mTargets.get(1);
         mLastSplitTarget = mTargets.get(mTargets.size() - 2);
         mDismissStartTarget = mTargets.get(0);
@@ -269,28 +285,31 @@
         return mTargets.get(minIndex);
     }
 
-    private void calculateTargets(boolean isLeftRightSplit, int dockedSide) {
+    private void calculateTargets() {
         mTargets.clear();
-        int dividerMax = isLeftRightSplit
+        int dividerMax = mIsLeftRightSplit
                 ? mDisplayWidth
                 : mDisplayHeight;
         int startPos = -mDividerSize;
-        if (dockedSide == DOCKED_RIGHT) {
+        if (mDockSide == DOCKED_RIGHT) {
             startPos += mInsets.left;
         }
         mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
         switch (mSnapMode) {
             case SNAP_MODE_16_9:
-                addRatio16_9Targets(isLeftRightSplit, dividerMax);
+                addRatio16_9Targets(mIsLeftRightSplit, dividerMax);
                 break;
             case SNAP_FIXED_RATIO:
-                addFixedDivisionTargets(isLeftRightSplit, dividerMax);
+                addFixedDivisionTargets(mIsLeftRightSplit, dividerMax);
                 break;
             case SNAP_ONLY_1_1:
-                addMiddleTarget(isLeftRightSplit);
+                addMiddleTarget(mIsLeftRightSplit);
                 break;
             case SNAP_MODE_MINIMIZED:
-                addMinimizedTarget(isLeftRightSplit, dockedSide);
+                addMinimizedTarget(mIsLeftRightSplit, mDockSide);
+                break;
+            case SNAP_FLEXIBLE_SPLIT:
+                addFlexSplitTargets(mIsLeftRightSplit, dividerMax);
                 break;
         }
         mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
@@ -299,9 +318,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,19 +332,35 @@
         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) {
+        int 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);
     }
 
+    private void addFlexSplitTargets(boolean isLeftRightSplit, int dividerMax) {
+        int start = 0;
+        int end = isLeftRightSplit ? mDisplayWidth : mDisplayHeight;
+        int pinnedTaskbarShiftStart = isLeftRightSplit
+                ? mPinnedTaskbarInsets.left : mPinnedTaskbarInsets.top;
+        int pinnedTaskbarShiftEnd = isLeftRightSplit
+                ? mPinnedTaskbarInsets.right : mPinnedTaskbarInsets.bottom;
+
+        float ratio = areOffscreenRatiosSupported()
+                ? SplitLayout.OFFSCREEN_ASYMMETRIC_RATIO
+                : SplitLayout.ONSCREEN_ONLY_ASYMMETRIC_RATIO;
+        int size = (int) (ratio * (end - start)) - mDividerSize / 2;
+
+        int leftTopPosition = start + pinnedTaskbarShiftStart + size;
+        int rightBottomPosition = end - pinnedTaskbarShiftEnd - size - mDividerSize;
+        addNonDismissingTargets(isLeftRightSplit, leftTopPosition, rightBottomPosition, dividerMax);
+    }
+
     private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) {
         int start = isLeftRightSplit ? mInsets.left : mInsets.top;
         int end = isLeftRightSplit
@@ -347,7 +382,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..dab30b0 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;
@@ -48,10 +49,13 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.view.Display;
+import android.view.InsetsController;
+import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.RoundedCorner;
@@ -77,7 +81,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 +103,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
@@ -147,6 +156,7 @@
     private final ResizingEffectPolicy mSurfaceEffectPolicy;
     private final ShellTaskOrganizer mTaskOrganizer;
     private final InsetsState mInsetsState = new InsetsState();
+    private Insets mPinnedTaskbarInsets = Insets.NONE;
 
     private Context mContext;
     @VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
@@ -517,6 +527,7 @@
     @Override
     public void insetsChanged(InsetsState insetsState) {
         mInsetsState.set(insetsState);
+
         if (!mInitialized) {
             return;
         }
@@ -525,9 +536,51 @@
             // flicker.
             return;
         }
+
+        // Check to see if insets changed in such a way that the divider algorithm needs to be
+        // recalculated.
+        Insets pinnedTaskbarInsets = calculatePinnedTaskbarInsets(insetsState);
+        if (!mPinnedTaskbarInsets.equals(pinnedTaskbarInsets)) {
+            mPinnedTaskbarInsets = pinnedTaskbarInsets;
+            // Refresh the DividerSnapAlgorithm.
+            mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+            // If the divider is no longer placed on a snap point, animate it to the nearest one.
+            DividerSnapAlgorithm.SnapTarget snapTarget =
+                    findSnapTarget(mDividerPosition, 0, false /* hardDismiss */);
+            if (snapTarget.position != mDividerPosition) {
+                snapToTarget(mDividerPosition, snapTarget,
+                        InsetsController.ANIMATION_DURATION_RESIZE,
+                        InsetsController.RESIZE_INTERPOLATOR);
+            }
+        }
+
         mSplitWindowManager.onInsetsChanged(insetsState);
     }
 
+    /**
+     * Calculates the insets that might trigger a divider algorithm recalculation. Currently, only
+     * pinned Taskbar does this, and only when the IME is not showing.
+     */
+    private Insets calculatePinnedTaskbarInsets(InsetsState insetsState) {
+        if (insetsState.isSourceOrDefaultVisible(InsetsSource.ID_IME, WindowInsets.Type.ime())) {
+            return Insets.NONE;
+        }
+
+        // If IME is not showing...
+        for (int i = insetsState.sourceSize() - 1; i >= 0; i--) {
+            final InsetsSource source = insetsState.sourceAt(i);
+            // and Taskbar is pinned...
+            if (source.getType() == WindowInsets.Type.navigationBars()
+                    && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) {
+                // Return Insets representing the pinned taskbar state.
+                return source.calculateVisibleInsets(mRootBounds);
+            }
+        }
+
+        // Else, divider can calculate based on the full display.
+        return Insets.NONE;
+    }
+
     @Override
     public void insetsControlChanged(InsetsState insetsState,
             InsetsSourceControl[] activeControls) {
@@ -604,25 +657,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, int, Interpolator)}, 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);
@@ -667,6 +730,7 @@
                 mDividerSize,
                 mIsLeftRightSplit,
                 insets,
+                mPinnedTaskbarInsets.toRect(),
                 mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
     }
 
@@ -674,14 +738,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 +763,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 +790,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 +1114,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/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
index 688f8ca..49c2785 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java
@@ -68,7 +68,7 @@
 
     private void setViewVisibility(@IdRes int resId, boolean show) {
         final View view = findViewById(resId);
-        int visibility = show ? View.VISIBLE : View.GONE;
+        int visibility = show ? View.VISIBLE : View.INVISIBLE;
         if (view.getVisibility() == visibility) {
             return;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
index b141beb..fd1bbc4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
@@ -100,7 +100,7 @@
 
     private void setViewVisibility(@IdRes int resId, boolean show) {
         final View view = findViewById(resId);
-        int visibility = show ? View.VISIBLE : View.GONE;
+        int visibility = show ? View.VISIBLE : View.INVISIBLE;
         if (view.getVisibility() == visibility) {
             return;
         }
@@ -171,7 +171,7 @@
         fadeOut.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                view.setVisibility(View.GONE);
+                view.setVisibility(View.INVISIBLE);
             }
         });
         fadeOut.start();
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 0942e05..fec4c16 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;
@@ -85,8 +86,13 @@
 import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
 import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
 import com.android.wm.shell.desktopmode.education.AppHandleEducationFilter;
+import com.android.wm.shell.desktopmode.education.AppToWebEducationController;
+import com.android.wm.shell.desktopmode.education.AppToWebEducationFilter;
 import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
+import com.android.wm.shell.desktopmode.education.data.AppToWebEducationDatastoreRepository;
 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;
@@ -129,6 +135,7 @@
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
+import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationPromoController;
 import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController;
 import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
 
@@ -153,12 +160,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 {
 
     //
@@ -173,8 +175,7 @@
 
     @WMSingleton
     @Provides
-    static BubblePositioner provideBubblePositioner(Context context,
-            WindowManager windowManager) {
+    static BubblePositioner provideBubblePositioner(Context context, WindowManager windowManager) {
         return new BubblePositioner(context, windowManager);
     }
 
@@ -186,20 +187,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,
@@ -224,14 +227,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);
     }
 
@@ -265,6 +292,7 @@
             MultiInstanceHelper multiInstanceHelper,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             AppHandleEducationController appHandleEducationController,
+            AppToWebEducationController appToWebEducationController,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler,
             FocusTransitionObserver focusTransitionObserver,
@@ -294,6 +322,7 @@
                     multiInstanceHelper,
                     desktopTasksLimiter,
                     appHandleEducationController,
+                    appToWebEducationController,
                     windowDecorCaptionHandleRepository,
                     desktopActivityOrientationHandler,
                     focusTransitionObserver,
@@ -318,9 +347,7 @@
     @WMSingleton
     @Provides
     static AppToWebGenericLinksParser provideGenericLinksParser(
-            Context context,
-            @ShellMainThread ShellExecutor mainExecutor
-    ) {
+            Context context, @ShellMainThread ShellExecutor mainExecutor) {
         return new AppToWebGenericLinksParser(context, mainExecutor);
     }
 
@@ -328,8 +355,7 @@
     static AssistContentRequester provideAssistContentRequester(
             Context context,
             @ShellMainThread ShellExecutor shellExecutor,
-            @ShellBackgroundThread ShellExecutor bgExecutor
-    ) {
+            @ShellBackgroundThread ShellExecutor bgExecutor) {
         return new AssistContentRequester(context, shellExecutor, bgExecutor);
     }
 
@@ -366,15 +392,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
@@ -385,10 +416,7 @@
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellAnimationThread ShellExecutor animExecutor) {
         return new FreeformTaskTransitionHandler(
-                transitions,
-                displayController,
-                mainExecutor,
-                animExecutor);
+                transitions, displayController, mainExecutor, animExecutor);
     }
 
     @WMSingleton
@@ -402,8 +430,13 @@
             Optional<TaskChangeListener> taskChangeListener,
             FocusTransitionObserver focusTransitionObserver) {
         return new FreeformTaskTransitionObserver(
-                context, shellInit, transitions, desktopImmersiveController,
-                windowDecorViewModel, taskChangeListener, focusTransitionObserver);
+                context,
+                shellInit,
+                transitions,
+                desktopImmersiveController,
+                windowDecorViewModel,
+                taskChangeListener,
+                focusTransitionObserver);
     }
 
     @WMSingleton
@@ -419,8 +452,8 @@
         } else {
             transitionStarter = freeformTaskTransitionHandler;
         }
-        return new FreeformTaskTransitionStarterInitializer(shellInit, windowDecorViewModel,
-                transitionStarter);
+        return new FreeformTaskTransitionStarterInitializer(
+                shellInit, windowDecorViewModel, transitionStarter);
     }
 
     //
@@ -431,7 +464,8 @@
     @WMSingleton
     @Provides
     @DynamicOverride
-    static OneHandedController provideOneHandedController(Context context,
+    static OneHandedController provideOneHandedController(
+            Context context,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
             ShellController shellController,
@@ -443,9 +477,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);
     }
 
     //
@@ -477,12 +521,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);
     }
 
     //
@@ -502,10 +563,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
@@ -516,8 +583,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);
     }
 
     //
@@ -534,8 +605,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);
@@ -546,8 +616,7 @@
                 progressProvider.get(),
                 animators,
                 unfoldTransitionHandler,
-                mainExecutor
-        );
+                mainExecutor);
     }
 
     @Provides
@@ -555,10 +624,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
@@ -568,14 +636,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
@@ -602,8 +674,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
@@ -632,6 +711,7 @@
             Transitions transitions,
             KeyguardManager keyguardManager,
             ReturnToDragStartAnimator returnToDragStartAnimator,
+            Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
             EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
             ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
             DesktopModeDragAndDropTransitionHandler desktopModeDragAndDropTransitionHandler,
@@ -652,18 +732,39 @@
             FocusTransitionObserver focusTransitionObserver,
             DesktopModeEventLogger desktopModeEventLogger,
             DesktopTilingDecorViewModel desktopTilingDecorViewModel) {
-        return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
-                displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
-                dragAndDropController, transitions, keyguardManager,
-                returnToDragStartAnimator, enterDesktopTransitionHandler,
-                exitDesktopTransitionHandler, desktopModeDragAndDropTransitionHandler,
+        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);
     }
 
@@ -693,10 +794,11 @@
 
     @WMSingleton
     @Provides
-    static Optional<TaskChangeListener> provideDesktopTaskChangeListener(Context context) {
-        if (Flags.enableWindowingTransitionHandlersObservers() &&
-                DesktopModeStatus.canEnterDesktopMode(context)) {
-            return Optional.of(new DesktopTaskChangeListener());
+    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();
     }
@@ -724,8 +826,7 @@
                         maxTaskLimit,
                         interactionJankMonitor,
                         context,
-                        handler)
-        );
+                        handler));
     }
 
     @WMSingleton
@@ -739,10 +840,7 @@
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.of(
                     new DesktopImmersiveController(
-                            transitions,
-                            desktopRepository,
-                            displayController,
-                            shellTaskOrganizer));
+                            transitions, desktopRepository, displayController, shellTaskOrganizer));
         }
         return Optional.empty();
     }
@@ -750,11 +848,10 @@
     @WMSingleton
     @Provides
     static ReturnToDragStartAnimator provideReturnToDragStartAnimator(
-            Context context, InteractionJankMonitor interactionJankMonitor) {
-        return new ReturnToDragStartAnimator(context, interactionJankMonitor);
+            InteractionJankMonitor interactionJankMonitor) {
+        return new ReturnToDragStartAnimator(interactionJankMonitor);
     }
 
-
     @WMSingleton
     @Provides
     static DragToDesktopTransitionHandler provideDragToDesktopTransitionHandler(
@@ -762,12 +859,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
@@ -802,30 +899,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);
     }
 
@@ -837,12 +933,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();
     }
@@ -854,12 +954,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
@@ -870,8 +974,11 @@
             @DynamicOverride DesktopRepository desktopRepository,
             FreeformTaskTransitionHandler freeformTaskTransitionHandler,
             CloseDesktopTaskTransitionHandler closeDesktopTaskTransitionHandler,
+            Optional<DesktopImmersiveController> desktopImmersiveController,
             InteractionJankMonitor interactionJankMonitor,
-            @ShellMainThread Handler handler
+            @ShellMainThread Handler handler,
+            ShellInit shellInit,
+            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
     ) {
         if (!DesktopModeStatus.canEnterDesktopMode(context)) {
             return Optional.empty();
@@ -883,8 +990,11 @@
                         desktopRepository,
                         freeformTaskTransitionHandler,
                         closeDesktopTaskTransitionHandler,
+                        desktopImmersiveController.get(),
                         interactionJankMonitor,
-                        handler));
+                        handler,
+                        shellInit,
+                        rootTaskDisplayAreaOrganizer));
     }
 
     @WMSingleton
@@ -929,12 +1039,25 @@
     @Provides
     static DesktopWindowingEducationTooltipController
             provideDesktopWindowingEducationTooltipController(
+                    Context context,
+                    AdditionalSystemViewContainer.Factory additionalSystemViewContainerFactory,
+                    DisplayController displayController) {
+        return new DesktopWindowingEducationTooltipController(
+                context, additionalSystemViewContainerFactory, displayController);
+    }
+
+    @WMSingleton
+    @Provides
+    static DesktopWindowingEducationPromoController provideDesktopWindowingEducationPromoController(
             Context context,
             AdditionalSystemViewContainer.Factory additionalSystemViewContainerFactory,
             DisplayController displayController
     ) {
-        return new DesktopWindowingEducationTooltipController(context,
-                additionalSystemViewContainerFactory, displayController);
+        return new DesktopWindowingEducationPromoController(
+                context,
+                additionalSystemViewContainerFactory,
+                displayController
+        );
     }
 
     @OptIn(markerClass = ExperimentalCoroutinesApi.class)
@@ -946,22 +1069,67 @@
             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 AppToWebEducationDatastoreRepository provideAppToWebEducationDatastoreRepository(
+            Context context) {
+        return new AppToWebEducationDatastoreRepository(context);
+    }
+
+    @WMSingleton
+    @Provides
+    static AppToWebEducationFilter provideAppToWebEducationFilter(
+            Context context,
+            AppToWebEducationDatastoreRepository appToWebEducationDatastoreRepository) {
+        return new AppToWebEducationFilter(context, appToWebEducationDatastoreRepository);
+    }
+
+    @OptIn(markerClass = ExperimentalCoroutinesApi.class)
+    @WMSingleton
+    @Provides
+    static AppToWebEducationController provideAppToWebEducationController(
+            Context context,
+            AppToWebEducationFilter appToWebEducationFilter,
+            AppToWebEducationDatastoreRepository appToWebEducationDatastoreRepository,
+            WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
+            DesktopWindowingEducationPromoController desktopWindowingEducationPromoController,
+            @ShellMainThread CoroutineScope applicationScope,
+            @ShellBackgroundThread MainCoroutineDispatcher backgroundDispatcher) {
+        return new AppToWebEducationController(context, appToWebEducationFilter,
+                appToWebEducationDatastoreRepository, windowDecorCaptionHandleRepository,
+                desktopWindowingEducationPromoController, 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
     //
@@ -969,14 +1137,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,
@@ -987,9 +1155,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);
     }
 
     //
@@ -1003,8 +1180,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..f69aa6d 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
     }
 
 
@@ -213,9 +224,9 @@
         finishTransaction: SurfaceControl.Transaction,
         finishCallback: Transitions.TransitionFinishCallback
     ): Boolean {
-        logD("startAnimation transition=%s", transition)
         val state = requireState()
         if (transition != state.transition) return false
+        logD("startAnimation transition=%s", transition)
         animateResize(
             targetTaskId = state.taskId,
             info = info,
@@ -323,7 +334,6 @@
         startTransaction: SurfaceControl.Transaction,
         finishTransaction: SurfaceControl.Transaction,
     ) {
-        logD("onTransitionReady transition=%s", transition)
         // Check if this is a pending external exit transition.
         val pendingExit = pendingExternalExitTransitions
             .firstOrNull { pendingExit -> pendingExit.transition == transition }
@@ -391,7 +401,6 @@
     }
 
     override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
-        logD("onTransitionMerged merged=%s playing=%s", merged, playing)
         val pendingExit = pendingExternalExitTransitions
             .firstOrNull { pendingExit -> pendingExit.transition == merged }
         if (pendingExit != null) {
@@ -404,7 +413,6 @@
     }
 
     override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
-        logD("onTransitionFinished transition=%s aborted=%b", transition, aborted)
         val pendingExit = pendingExternalExitTransitions
             .firstOrNull { pendingExit -> pendingExit.transition == transition }
         if (pendingExit != null) {
@@ -461,6 +469,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..48bb2a8 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
@@ -25,17 +25,24 @@
 import android.view.WindowManager
 import android.window.DesktopModeFlags
 import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
 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.RootTaskDisplayAreaOrganizer
 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.TransitionUtil
 import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
 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 +51,20 @@
     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,
+    shellInit: ShellInit,
+    private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
 ) : MixedTransitionHandler, FreeformTaskTransitionStarter {
 
+    init {
+        shellInit.addInitCallback ({ transitions.addHandler(this) }, this)
+    }
+
+    @VisibleForTesting
+    val pendingMixedTransitions = mutableListOf<PendingMixedTransition>()
+
     /** Delegates starting transition to [FreeformTaskTransitionHandler]. */
     override fun startWindowingModeTransition(
         targetWindowingMode: Int,
@@ -65,6 +82,48 @@
         }
         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,
+        minimizingTaskId: Int? = null,
+        exitingImmersiveTask: Int? = null,
+    ): IBinder {
+        if (!Flags.enableFullyImmersiveInDesktop() &&
+            !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
+            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,
+                    minimizingTask = minimizingTaskId,
+                    exitingImmersiveTask = exitingImmersiveTask,
+                ))
+            }
+    }
+
+    /** Notifies this handler that there is a pending transition for it to handle. */
+    fun addPendingMixedTransition(pendingMixedTransition: PendingMixedTransition) {
+        pendingMixedTransitions.add(pendingMixedTransition)
     }
 
     /** Returns null, as it only handles transitions started from Shell. */
@@ -78,11 +137,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 {
+                logV("No 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 +197,85 @@
         )
     }
 
+    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 minimizeChange = pending.minimizingTask?.let { minimizingTask ->
+            findDesktopTaskChange(info, minimizingTask)
+        }
+        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 mixed launch transition task#%d, minimizingTask#%s immersiveExitTask#%s",
+            launchChange.taskInfo!!.taskId, minimizeChange?.taskInfo?.taskId,
+            immersiveExitChange?.taskInfo?.taskId
+        )
+        if (DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
+            // Only apply minimize change reparenting here if we implement the new app launch
+            // transitions, otherwise this reparenting is handled in the default handler.
+            minimizeChange?.let {
+                applyMinimizeChangeReparenting(info, minimizeChange, startTransaction)
+            }
+        }
+        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.
@@ -116,7 +286,7 @@
         change: TransitionInfo.Change,
         startTransaction: SurfaceControl.Transaction,
         finishTransaction: SurfaceControl.Transaction,
-        finishCallback: Transitions.TransitionFinishCallback,
+        finishCallback: TransitionFinishCallback,
     ): Boolean {
         // Starting the jank trace if closing the last window in desktop mode.
         interactionJankMonitor.begin(
@@ -126,14 +296,56 @@
             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)
+            }
+        )
+    }
+
+    /**
+     * Reparent the minimizing task back to its root display area.
+     *
+     * During the launch/minimize animation the all animated tasks will be reparented to a
+     * transition leash shown in front of other desktop tasks. Reparenting the minimizing task back
+     * to its root display area ensures that task stays behind other desktop tasks during the
+     * animation.
+     */
+    private fun applyMinimizeChangeReparenting(
+        info: TransitionInfo,
+        minimizeChange: Change,
+        startTransaction: SurfaceControl.Transaction,
+    ) {
+        require(TransitionUtil.isOpeningMode(info.type))
+        require(minimizeChange.taskInfo != null)
+        val taskInfo = minimizeChange.taskInfo!!
+        require(taskInfo.isFreeform)
+        logV("Reparenting minimizing task#%d", taskInfo.taskId)
+        rootTaskDisplayAreaOrganizer.reparentToDisplayArea(
+            taskInfo.displayId, minimizeChange.leash, startTransaction)
+    }
+
+    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 +367,44 @@
         }
     }
 
+    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 minimizingTask: 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/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 61de077..09e77fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR;
@@ -259,6 +260,7 @@
                         FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
         lp.setTitle("Desktop Mode Visual Indicator");
         lp.setTrustedOverlay();
+        lp.inputFeatures |= INPUT_FEATURE_NO_INPUT_CHANNEL;
         final WindowlessWindowManager windowManager = new WindowlessWindowManager(
                 mTaskInfo.configuration, mLeash,
                 null /* hostInputToken */);
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 6f7b716..fda709a 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
@@ -23,14 +23,14 @@
 import android.util.ArraySet
 import android.util.SparseArray
 import android.view.Display.INVALID_DISPLAY
+import android.window.DesktopModeFlags
 import android.window.WindowContainerToken
 import androidx.core.util.forEach
 import androidx.core.util.keyIterator
 import androidx.core.util.valueIterator
 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,35 +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
-
-            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++
-                        addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId)
-                        addActiveTask(desktop.displayId, task.taskId)
-                        updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
-                    } else {
-                        addActiveTask(desktop.displayId, task.taskId)
-                        updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
-                        minimizeTask(desktop.displayId, task.taskId)
-                    }
-                }
-        }
+        repositoryInitializer.initialize(this)
     }
 
     /** Adds [activeTasksListener] to be notified of updates to active tasks. */
@@ -204,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)
 
@@ -219,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)
             }
@@ -292,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) {
@@ -302,7 +282,7 @@
             return
         }
         val prevCount = getVisibleTaskCount(displayId)
-        if (visible) {
+        if (isVisible) {
             desktopTaskDataByDisplayId.getOrCreate(displayId).visibleTasks.add(taskId)
             unminimizeTask(displayId, taskId)
         } else {
@@ -311,9 +291,12 @@
         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)
+            if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
+                updatePersistentRepository(displayId)
+            }
         }
     }
 
@@ -355,13 +338,13 @@
      *
      * 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)
         // Unminimize the task if it is minimized.
         unminimizeTask(displayId, taskId)
-        if (Flags.enableDesktopWindowingPersistence()) {
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
             updatePersistentRepository(displayId)
         }
     }
@@ -378,8 +361,8 @@
             logD("Minimize Task: display=%d, task=%d", displayId, taskId)
             desktopTaskDataByDisplayId.getOrCreate(displayId).minimizedTasks.add(taskId)
         }
-
-        if (Flags.enableDesktopWindowingPersistence()) {
+        updateTask(displayId, taskId, isVisible = false)
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
             updatePersistentRepository(displayId)
         }
     }
@@ -426,8 +409,8 @@
         // Remove task from unminimized task if it is minimized.
         unminimizeTask(displayId, taskId)
         removeActiveTask(taskId)
-        updateTaskVisibility(displayId, taskId, visible = false)
-        if (Flags.enableDesktopWindowingPersistence()) {
+        removeVisibleTask(taskId)
+        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
             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 eec2ba5..75f8839 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
@@ -54,6 +54,7 @@
 import android.view.WindowManager.TRANSIT_NONE
 import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.widget.Toast
 import android.window.DesktopModeFlags
 import android.window.DesktopModeFlags.DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE
 import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
@@ -87,6 +88,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
@@ -146,6 +148,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,
@@ -379,7 +382,7 @@
         // TODO(342378842): Instead of using default display, support multiple displays
         val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
             DEFAULT_DISPLAY, wct, taskId)
-        val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
+        val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
             wct = wct,
             displayId = DEFAULT_DISPLAY,
             excludeTaskId = taskId,
@@ -393,7 +396,7 @@
         // TODO(343149901): Add DPI changes for task launch
         val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
         taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
-        runOnTransit?.invoke(transition)
+        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
         return true
     }
 
@@ -410,7 +413,7 @@
         }
         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,
@@ -422,7 +425,7 @@
 
         val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
         taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
-        runOnTransit?.invoke(transition)
+        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
     }
 
     /**
@@ -459,12 +462,12 @@
         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 {
             taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) }
-            runOnTransit?.invoke(transition)
+            exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
         }
     }
 
@@ -507,7 +510,8 @@
                 taskId
             )
         )
-        return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo)
+        return desktopImmersiveController.exitImmersiveIfApplicable(wct, taskInfo).asExit()
+            ?.runOnTransitionStart
     }
 
     fun minimizeTask(taskInfo: RunningTaskInfo) {
@@ -520,7 +524,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)
@@ -531,7 +535,7 @@
                 taskId = taskId
             )
         }
-        runOnTransit?.invoke(transition)
+        exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
     }
 
     /** Move a task with given `taskId` to fullscreen */
@@ -627,7 +631,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,
@@ -638,8 +642,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)
     }
 
     /**
@@ -658,32 +667,40 @@
         }
         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,
-                taskInfo.displayId
-            )
-        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 taskIdToMinimize = addAndGetMinimizeChanges(displayId, wct, taskId)
         if (remoteTransition == null) {
-            val t = transitions.startTransition(transitionType, wct, null /* handler */)
+            val t = desktopMixedTransitionHandler.startLaunchTransition(
+                transitionType = transitionType,
+                wct = wct,
+                taskId = taskId,
+                minimizingTaskId = taskIdToMinimize,
+                exitingImmersiveTask = exitingImmersiveTask,
+            )
             taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
             return t
         }
@@ -698,7 +715,7 @@
                 mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize)
         val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
         remoteTransitionHandler.setTransition(t)
-        taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
+        taskIdToMinimize.let { addPendingMinimizeTransition(t, it) }
         return t
     }
 
@@ -842,6 +859,38 @@
         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,
+                )
+            }
+            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)
@@ -964,7 +1013,6 @@
                     taskSurface,
                     startBounds = currentDragBounds,
                     endBounds = destinationBounds,
-                    isResizable = taskInfo.isResizeable,
                 )
             }
             return
@@ -998,7 +1046,13 @@
                 taskSurface,
                 startBounds = currentDragBounds,
                 endBounds = dragStartBounds,
-                isResizable = taskInfo.isResizeable,
+                doOnEnd = {
+                    Toast.makeText(
+                        context,
+                        com.android.wm.shell.R.string.desktop_mode_non_resizable_snap_text,
+                        Toast.LENGTH_SHORT
+                    ).show()
+                },
             )
         } else {
             val resizeTrigger = if (position == SnapPosition.LEFT) {
@@ -1108,7 +1162,7 @@
                 if (runningTaskInfo != null) {
                     // Task is already running, reorder it to the front
                     wct.reorder(runningTaskInfo.token, /* onTop= */ true)
-                } else if (Flags.enableDesktopWindowingPersistence()) {
+                } else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
                     // Task is not running, start it
                     wct.startTask(
                         taskId,
@@ -1250,10 +1304,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
@@ -1376,14 +1427,16 @@
             wct.startTask(requestedTaskId, options.toBundle())
             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)
             taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
-            runOnTransit?.invoke(transition)
+            addPendingAppLaunchTransition(transition, requestedTaskId, taskIdToMinimize)
+            exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
         } else {
             val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
             splitScreenController.startTask(requestedTaskId, splitPosition,
@@ -1523,6 +1576,7 @@
         desktopImmersiveController.exitImmersiveIfApplicable(transition, wct, task.displayId)
         // 2) minimize a Task if needed.
         val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+        addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
         if (taskIdToMinimize != null) {
             addPendingMinimizeTransition(transition, taskIdToMinimize)
             return wct
@@ -1554,6 +1608,7 @@
                 // minimize another Task.
                 val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
                 taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+                addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
                 desktopImmersiveController.exitImmersiveIfApplicable(
                     transition, wct, task.displayId
                 )
@@ -1579,10 +1634,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
@@ -1603,7 +1655,7 @@
         taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
             doesAnyTaskRequireTaskbarRounding(
                 task.displayId,
-                task.id
+                task.taskId
             )
         )
         return if (wct.isEmpty) null else wct
@@ -1737,6 +1789,20 @@
         }
     }
 
+    private fun addPendingAppLaunchTransition(
+        transition: IBinder,
+        launchTaskId: Int,
+        minimizeTaskId: Int?,
+    ) {
+        if (!DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
+            return
+        }
+        // TODO b/359523924: pass immersive task here?
+        desktopMixedTransitionHandler.addPendingMixedTransition(
+            DesktopMixedTransitionHandler.PendingMixedTransition.Launch(
+                transition, launchTaskId, minimizeTaskId, /* exitingImmersiveTask= */ null))
+    }
+
     fun removeDesktop(displayId: Int) {
         if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
 
@@ -1908,11 +1974,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(
@@ -1937,17 +2007,32 @@
                 )
             }
             IndicatorType.NO_INDICATOR -> {
+                // Create a copy so that we can animate from the current bounds if we end up having
+                // to snap the surface back without a WCT change.
+                val destinationBounds = Rect(currentDragBounds)
                 // If task bounds are outside valid drag area, snap them inward
                 DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(
-                    currentDragBounds,
+                    destinationBounds,
                     validDragArea
                 )
 
-                if (currentDragBounds == dragStartBounds) return
+                if (destinationBounds == dragStartBounds) {
+                    // There's no actual difference between the start and end bounds, so while a
+                    // WCT change isn't needed, the dragged surface still needs to be snapped back
+                    // to its original location.
+                    releaseVisualIndicator()
+                    returnToDragStartAnimator.start(
+                        taskInfo.taskId,
+                        taskSurface,
+                        startBounds = currentDragBounds,
+                        endBounds = dragStartBounds,
+                    )
+                    return
+                }
 
                 // Update task bounds so that the task position will match the position of its leash
                 val wct = WindowContainerTransaction()
-                wct.setBounds(taskInfo.token, currentDragBounds)
+                wct.setBounds(taskInfo.token, destinationBounds)
                 transitions.startTransition(TRANSIT_CHANGE, wct, null)
 
                 releaseVisualIndicator()
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 7bcc5d1..f0e3a2b 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
@@ -133,7 +133,6 @@
         }
 
         override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
-            logV("transition %s finished", transition)
             if (activeTransitionTokensAndTasks.remove(transition) != null) {
                 if (aborted) {
                     interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
@@ -252,10 +251,6 @@
         ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
     }
 
-    private fun logE(msg: String, vararg arguments: Any?) {
-        ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
-    }
-
     private companion object {
         const val TAG = "DesktopTasksLimiter"
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
index dedd44f..d537da8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -60,7 +60,8 @@
  * entering and exiting freeform.
  */
 public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionHandler {
-    private static final int FULLSCREEN_ANIMATION_DURATION = 336;
+    @VisibleForTesting
+    static final int FULLSCREEN_ANIMATION_DURATION = 336;
 
     private final Context mContext;
     private final Transitions mTransitions;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
index f4df42c..4e08d10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
@@ -19,28 +19,24 @@
 import android.animation.Animator
 import android.animation.RectEvaluator
 import android.animation.ValueAnimator
-import android.content.Context
 import android.graphics.Rect
 import android.view.SurfaceControl
-import android.widget.Toast
 import androidx.core.animation.addListener
 import com.android.internal.jank.Cuj
 import com.android.internal.jank.InteractionJankMonitor
-import com.android.wm.shell.R
 import com.android.wm.shell.windowdecor.OnTaskRepositionAnimationListener
 import java.util.function.Supplier
 
 /** Animates the task surface moving from its current drag position to its pre-drag position. */
 class ReturnToDragStartAnimator(
-    private val context: Context,
     private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
     private val interactionJankMonitor: InteractionJankMonitor
 ) {
     private var boundsAnimator: Animator? = null
     private lateinit var taskRepositionAnimationListener: OnTaskRepositionAnimationListener
 
-    constructor(context: Context, interactionJankMonitor: InteractionJankMonitor) :
-            this(context, Supplier { SurfaceControl.Transaction() }, interactionJankMonitor)
+    constructor(interactionJankMonitor: InteractionJankMonitor) :
+            this(Supplier { SurfaceControl.Transaction() }, interactionJankMonitor)
 
     /** Sets a listener for the start and end of the reposition animation. */
     fun setTaskRepositionAnimationListener(listener: OnTaskRepositionAnimationListener) {
@@ -53,7 +49,7 @@
         taskSurface: SurfaceControl,
         startBounds: Rect,
         endBounds: Rect,
-        isResizable: Boolean
+        doOnEnd: (() -> Unit)? = null,
     ) {
         val tx = transactionSupplier.get()
 
@@ -87,13 +83,7 @@
                                 .apply()
                             taskRepositionAnimationListener.onAnimationEnd(taskId)
                             boundsAnimator = null
-                            if (!isResizable) {
-                                Toast.makeText(
-                                    context,
-                                    R.string.desktop_mode_non_resizable_snap_text,
-                                    Toast.LENGTH_SHORT
-                                ).show()
-                            }
+                            doOnEnd?.invoke()
                             interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)
                         }
                     )
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepository.kt
index 7ae5370..8bfcca0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepository.kt
@@ -18,6 +18,10 @@
 
 import android.app.ActivityManager.RunningTaskInfo
 import android.graphics.Rect
+import com.android.wm.shell.desktopmode.CaptionState.AppHandle
+import com.android.wm.shell.desktopmode.CaptionState.AppHeader
+import com.android.wm.shell.desktopmode.CaptionState.NoCaption
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 
@@ -26,11 +30,20 @@
   private val _captionStateFlow = MutableStateFlow<CaptionState>(CaptionState.NoCaption)
   /** Observer for app handle state changes. */
   val captionStateFlow: StateFlow<CaptionState> = _captionStateFlow
+  private val _appToWebUsageFlow = MutableSharedFlow<Unit>()
+  /** Observer for App-to-Web usage. */
+  val appToWebUsageFlow = _appToWebUsageFlow
+
 
   /** Notifies [captionStateFlow] if there is a change to caption state. */
   fun notifyCaptionChanged(captionState: CaptionState) {
     _captionStateFlow.value = captionState
   }
+
+  /** Notifies [appToWebUsageFlow] if App-to-Web feature is used. */
+  fun onAppToWebUsage() {
+    _appToWebUsageFlow.tryEmit(Unit)
+  }
 }
 
 /**
@@ -41,17 +54,19 @@
  * * [AppHeader]: Indicating that there is at least one visible app chip on the screen.
  * * [NoCaption]: Signifying that no caption handle is currently visible on the device.
  */
-sealed class CaptionState {
+sealed class CaptionState{
   data class AppHandle(
-      val runningTaskInfo: RunningTaskInfo,
-      val isHandleMenuExpanded: Boolean,
-      val globalAppHandleBounds: Rect
+    val runningTaskInfo: RunningTaskInfo,
+    val isHandleMenuExpanded: Boolean,
+    val globalAppHandleBounds: Rect,
+    val isCapturedLinkAvailable: Boolean
   ) : CaptionState()
 
   data class AppHeader(
-      val runningTaskInfo: RunningTaskInfo,
-      val isHeaderMenuExpanded: Boolean,
-      val globalAppChipBounds: Rect
+    val runningTaskInfo: RunningTaskInfo,
+    val isHeaderMenuExpanded: Boolean,
+    val globalAppChipBounds: Rect,
+    val isCapturedLinkAvailable: Boolean
   ) : CaptionState()
 
   data object NoCaption : CaptionState()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
index f21a124..e01c448 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
@@ -36,7 +36,7 @@
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.common.Theme
 import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController
-import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.EducationViewConfig
+import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipEducationViewConfig
 import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
@@ -137,7 +137,7 @@
     // TODO: b/370546801 - Differentiate between user dismissing the tooltip vs following the cue.
     // Populate information important to inflate app handle education tooltip.
     val appHandleTooltipConfig =
-        EducationViewConfig(
+        TooltipEducationViewConfig(
             tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip,
             tooltipColorScheme = tooltipColorScheme,
             tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
@@ -196,7 +196,7 @@
                       windowingOptionPillHeight / 2)
           // Populate information important to inflate windowing image button education tooltip.
           val windowingImageButtonTooltipConfig =
-              EducationViewConfig(
+              TooltipEducationViewConfig(
                   tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
                   tooltipColorScheme = tooltipColorScheme,
                   tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
@@ -249,7 +249,7 @@
                   globalAppChipBounds.top + globalAppChipBounds.height() / 2)
           // Populate information important to inflate exit desktop mode education tooltip.
           val exitWindowingTooltipConfig =
-              EducationViewConfig(
+              TooltipEducationViewConfig(
                   tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
                   tooltipColorScheme = tooltipColorScheme,
                   tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt
new file mode 100644
index 0000000..693da81
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt
@@ -0,0 +1,207 @@
+/*
+ * 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.education
+
+import android.annotation.DimenRes
+import android.annotation.StringRes
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Point
+import android.os.SystemProperties
+import androidx.compose.ui.graphics.toArgb
+import com.android.window.flags.Flags
+import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.CaptionState
+import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository
+import com.android.wm.shell.desktopmode.education.data.AppToWebEducationDatastoreRepository
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode
+import com.android.wm.shell.windowdecor.common.DecorThemeUtil
+import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationPromoController
+import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationPromoController.EducationColorScheme
+import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationPromoController.EducationViewConfig
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.debounce
+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.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/**
+ * Controls App-to-Web education end to end.
+ *
+ * Listen to usages of App-to-Web, calls an api to check if the education
+ * should be shown and controls education UI.
+ */
+@OptIn(kotlinx.coroutines.FlowPreview::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class AppToWebEducationController(
+    private val context: Context,
+    private val appToWebEducationFilter: AppToWebEducationFilter,
+    private val appToWebEducationDatastoreRepository: AppToWebEducationDatastoreRepository,
+    private val windowDecorCaptionHandleRepository: WindowDecorCaptionHandleRepository,
+    private val windowingEducationViewController: DesktopWindowingEducationPromoController,
+    @ShellMainThread private val applicationCoroutineScope: CoroutineScope,
+    @ShellBackgroundThread private val backgroundDispatcher: MainCoroutineDispatcher,
+) {
+    private val decorThemeUtil = DecorThemeUtil(context)
+
+    init {
+        runIfEducationFeatureEnabled {
+            applicationCoroutineScope.launch {
+                // Central block handling the App-to-Web's educational flow end-to-end.
+                isEducationViewLimitReachedFlow()
+                    .flatMapLatest { countExceedsMaximum ->
+                        if (countExceedsMaximum) {
+                            // If the education has been viewed the maximum amount of times then
+                            // return emptyFlow() that completes immediately. This will help us to
+                            // not listen to [captionHandleStateFlow] after the education should
+                            // not be shown.
+                            emptyFlow()
+                        } else {
+                            // Listen for changes to window decor's caption.
+                            windowDecorCaptionHandleRepository.captionStateFlow
+                                // Wait for few seconds before emitting the latest state.
+                                .debounce(APP_TO_WEB_EDUCATION_DELAY_MILLIS)
+                                .filter { captionState ->
+                                    captionState !is CaptionState.NoCaption &&
+                                            appToWebEducationFilter
+                                                .shouldShowAppToWebEducation(captionState)
+                                }
+                        }
+                    }
+                    .flowOn(backgroundDispatcher)
+                    .collectLatest { captionState ->
+                        val educationColorScheme = educationColorScheme(captionState)
+                        showEducation(captionState, educationColorScheme!!)
+                        // After showing first tooltip, increase count of education views
+                        appToWebEducationDatastoreRepository.updateEducationShownCount()
+                    }
+            }
+
+            applicationCoroutineScope.launch {
+                if (isFeatureUsed()) return@launch
+                windowDecorCaptionHandleRepository.appToWebUsageFlow
+                    .collect {
+                        // If user utilizes App-to-Web, mark user has used the feature
+                        appToWebEducationDatastoreRepository
+                            .updateFeatureUsedTimestampMillis(isViewed = true)
+                    }
+            }
+        }
+    }
+
+    private inline fun runIfEducationFeatureEnabled(block: () -> Unit) {
+        if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppToWebEducation()) block()
+    }
+
+    private fun showEducation(captionState: CaptionState, colorScheme: EducationColorScheme) {
+        val educationGlobalCoordinates: Point
+        val taskId: Int
+        when (captionState) {
+            is CaptionState.AppHandle -> {
+                val appHandleBounds = captionState.globalAppHandleBounds
+                val educationWidth =
+                    loadDimensionPixelSize(R.dimen.desktop_windowing_education_promo_width)
+                educationGlobalCoordinates = Point(
+                    appHandleBounds.centerX() - educationWidth / 2,
+                    appHandleBounds.bottom
+                )
+                taskId = captionState.runningTaskInfo.taskId
+            }
+
+            is CaptionState.AppHeader -> {
+                val taskBounds =
+                    captionState.runningTaskInfo.configuration.windowConfiguration.bounds
+                educationGlobalCoordinates =
+                    Point(taskBounds.left, captionState.globalAppChipBounds.bottom)
+                taskId = captionState.runningTaskInfo.taskId
+            }
+
+            else -> return
+        }
+
+        // Populate information important to inflate education promo.
+        val educationConfig =
+            EducationViewConfig(
+                viewLayout = R.layout.desktop_windowing_education_promo,
+                educationColorScheme = colorScheme,
+                viewGlobalCoordinates = educationGlobalCoordinates,
+                educationText = getString(R.string.desktop_windowing_app_to_web_education_text),
+                widthId = R.dimen.desktop_windowing_education_promo_width,
+                heightId = R.dimen.desktop_windowing_education_promo_height
+            )
+
+        windowingEducationViewController.showEducation(
+            viewConfig = educationConfig, taskId = taskId)
+    }
+
+    private fun educationColorScheme(captionState: CaptionState): EducationColorScheme? {
+        val taskInfo: RunningTaskInfo = when (captionState) {
+            is CaptionState.AppHandle -> captionState.runningTaskInfo
+            is CaptionState.AppHeader -> captionState.runningTaskInfo
+            else -> return null
+        }
+
+        val colorScheme = decorThemeUtil.getColorScheme(taskInfo)
+        val tooltipContainerColor = colorScheme.surfaceBright.toArgb()
+        val tooltipTextColor = colorScheme.onSurface.toArgb()
+        return EducationColorScheme(tooltipContainerColor, tooltipTextColor)
+    }
+
+    /**
+     * Listens to changes in the number of times the education has been viewed, mapping the count to
+     * true if the education has been viewed the maximum amount of times.
+     */
+    private fun isEducationViewLimitReachedFlow(): Flow<Boolean> =
+        appToWebEducationDatastoreRepository.dataStoreFlow
+            .map { preferences ->
+                appToWebEducationFilter.isEducationViewLimitReached(preferences)}
+            .distinctUntilChanged()
+
+    /**
+     * Listens to the changes to [WindowingEducationProto#hasFeatureUsedTimestampMillis()] in
+     * datastore proto object.
+     */
+    private suspend fun isFeatureUsed(): Boolean =
+        appToWebEducationDatastoreRepository.dataStoreFlow.first().hasFeatureUsedTimestampMillis()
+
+    private fun loadDimensionPixelSize(@DimenRes resourceId: Int): Int {
+        if (resourceId == Resources.ID_NULL) return 0
+        return context.resources.getDimensionPixelSize(resourceId)
+    }
+
+    private fun getString(@StringRes resId: Int): String = context.resources.getString(resId)
+
+    companion object {
+        const val TAG = "AppToWebEducationController"
+        val APP_TO_WEB_EDUCATION_DELAY_MILLIS: Long
+            get() = SystemProperties.getLong(
+                "persist.windowing_app_handle_education_delay",
+                3000L
+            )
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt
new file mode 100644
index 0000000..feee6ed
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.education
+
+import android.annotation.IntegerRes
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.os.SystemClock
+import android.provider.Settings.Secure
+import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.CaptionState
+import com.android.wm.shell.desktopmode.education.data.AppToWebEducationDatastoreRepository
+import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
+import java.time.Duration
+
+/** Filters incoming App-to-Web education triggers based on set conditions. */
+class AppToWebEducationFilter(
+    private val context: Context,
+    private val appToWebEducationDatastoreRepository: AppToWebEducationDatastoreRepository
+) {
+
+    /** Returns true if conditions to show App-to-web education are met, returns false otherwise. */
+    suspend fun shouldShowAppToWebEducation(captionState: CaptionState): Boolean {
+        val (taskInfo: RunningTaskInfo, isCapturedLinkAvailable: Boolean) = when (captionState) {
+            is CaptionState.AppHandle ->
+                Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable)
+            is CaptionState.AppHeader ->
+                Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable)
+            else -> return false
+        }
+
+        val focusAppPackageName = taskInfo.topActivityInfo?.packageName ?: return false
+        val windowingEducationProto = appToWebEducationDatastoreRepository.windowingEducationProto()
+
+        return !isOtherEducationShowing() &&
+                !isEducationViewLimitReached(windowingEducationProto) &&
+                hasSufficientTimeSinceSetup() &&
+                !isFeatureUsedBefore(windowingEducationProto) &&
+                isCapturedLinkAvailable &&
+                isFocusAppInAllowlist(focusAppPackageName)
+    }
+
+    private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean =
+        focusAppPackageName in
+                context.resources.getStringArray(
+                    R.array.desktop_windowing_app_to_web_education_allowlist_apps)
+
+    // TODO: b/350953004 - Add checks based on App compat
+    // TODO: b/350951797 - Add checks based on PKT tips education
+    private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing() ||
+            isCompatUiEducationShowing()
+
+    private fun isTaskbarEducationShowing(): Boolean =
+        Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1
+
+    private fun isCompatUiEducationShowing(): Boolean =
+        Secure.getInt(context.contentResolver, Secure.COMPAT_UI_EDUCATION_SHOWING, 0) == 1
+
+    private fun hasSufficientTimeSinceSetup(): Boolean =
+        Duration.ofMillis(SystemClock.elapsedRealtime()) >
+                convertIntegerResourceToDuration(
+                    R.integer.desktop_windowing_education_required_time_since_setup_seconds)
+
+    /** Returns true if education is viewed maximum amount of times it should be shown. */
+    fun isEducationViewLimitReached(windowingEducationProto: WindowingEducationProto): Boolean =
+        windowingEducationProto.getAppToWebEducation().getEducationShownCount() >=
+                MAXIMUM_TIMES_EDUCATION_SHOWN
+
+    private fun isFeatureUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean =
+        windowingEducationProto.hasFeatureUsedTimestampMillis()
+
+    private fun convertIntegerResourceToDuration(@IntegerRes resourceId: Int): Duration =
+        Duration.ofSeconds(context.resources.getInteger(resourceId).toLong())
+
+    companion object {
+        private const val MAXIMUM_TIMES_EDUCATION_SHOWN = 100
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt
new file mode 100644
index 0000000..8be6e6d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.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.desktopmode.education.data
+
+import android.content.Context
+import android.util.Slog
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.core.Serializer
+import androidx.datastore.dataStoreFile
+import com.android.framework.protobuf.InvalidProtocolBufferException
+import com.android.internal.annotations.VisibleForTesting
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.first
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+
+/** Updates data in App-to-Web's education datastore. */
+class AppToWebEducationDatastoreRepository
+@VisibleForTesting
+constructor(private val dataStore: DataStore<WindowingEducationProto>) {
+    constructor(
+        context: Context
+    ) : this(
+        DataStoreFactory.create(
+            serializer = WindowingEducationProtoSerializer,
+            produceFile = { context.dataStoreFile(APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH) }))
+
+    /** Provides dataStore.data flow and handles exceptions thrown during collection */
+    val dataStoreFlow: Flow<WindowingEducationProto> =
+        dataStore.data.catch { exception ->
+            // dataStore.data throws an IOException when an error is encountered when reading data
+            if (exception is IOException) {
+                Slog.e(
+                    TAG,
+                    "Error in reading App-to-Web education related data from datastore," +
+                            "data is stored in a file named" +
+                            "$APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH", exception)
+            } else {
+                throw exception
+            }
+        }
+
+    /**
+     * Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the
+     * DataStore is empty or there's an error reading, it returns the default value of Proto.
+     */
+    suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first()
+
+    /**
+     * Updates [WindowingEducationProto.featureUsedTimestampMillis_] field in datastore with current
+     * timestamp if [isViewed] is true, if not then clears the field.
+     */
+    suspend fun updateFeatureUsedTimestampMillis(isViewed: Boolean) {
+        dataStore.updateData { preferences ->
+            if (isViewed) {
+                preferences
+                    .toBuilder().setFeatureUsedTimestampMillis(System.currentTimeMillis()).build()
+            } else {
+                preferences.toBuilder().clearFeatureUsedTimestampMillis().build()
+            }
+        }
+    }
+
+    /**
+     * Increases [AppToWebEducation.educationShownCount] field by one.
+     */
+    suspend fun updateEducationShownCount() {
+        val currentAppHandleProto = windowingEducationProto().appToWebEducation.toBuilder()
+        currentAppHandleProto
+            .setEducationShownCount(currentAppHandleProto.getEducationShownCount() + 1)
+        dataStore.updateData { preferences ->
+            preferences.toBuilder().setAppToWebEducation(currentAppHandleProto).build()
+        }
+    }
+
+
+    companion object {
+        private const val TAG = "AppToWebEducationDatastoreRepository"
+        private const val APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH = "app_to_web_education.pb"
+
+        object WindowingEducationProtoSerializer : Serializer<WindowingEducationProto> {
+
+            override val defaultValue: WindowingEducationProto =
+                WindowingEducationProto.getDefaultInstance()
+
+            override suspend fun readFrom(input: InputStream): WindowingEducationProto =
+                try {
+                    WindowingEducationProto.parseFrom(input)
+                } catch (exception: InvalidProtocolBufferException) {
+                    throw CorruptionException("Cannot read proto.", exception)
+                }
+
+            override suspend fun writeTo(
+                windowingProto: WindowingEducationProto,
+                output: OutputStream
+            ) = windowingProto.writeTo(output)
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto
index d29ec53..4cddd01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto
@@ -28,6 +28,8 @@
   oneof education_data {
     // Fields specific to app handle education
     AppHandleEducation app_handle_education = 3;
+    // Fields specific to App-to-Web education
+    AppToWebEducation app_to_web_education = 4;
   }
 
   message AppHandleEducation {
@@ -36,4 +38,9 @@
     // Timestamp of when app_usage_stats was last cached
     optional int64 app_usage_stats_last_update_timestamp_millis = 2;
   }
+
+  message AppToWebEducation {
+    // Number of times education is shown
+    optional int64 education_shown_count = 1;
+  }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
index 2d11e02..9e646f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
@@ -50,7 +50,9 @@
         DataStoreFactory.create(
             serializer = DesktopPersistentRepositoriesSerializer,
             produceFile = { context.dataStoreFile(DESKTOP_REPOSITORIES_DATASTORE_FILE) },
-            scope = bgCoroutineScope))
+            scope = bgCoroutineScope,
+        ),
+    )
 
     /** Provides `dataStore.data` flow and handles exceptions thrown during collection */
     private val dataStoreFlow: Flow<DesktopPersistentRepositories> =
@@ -116,7 +118,11 @@
                 val desktop =
                     getDesktop(currentRepository, desktopId)
                         .toBuilder()
-                        .updateTaskStates(visibleTasks, minimizedTasks)
+                        .updateTaskStates(
+                            visibleTasks,
+                            minimizedTasks,
+                            freeformTasksInZOrder,
+                        )
                         .updateZOrder(freeformTasksInZOrder)
 
                 desktopPersistentRepositories
@@ -169,9 +175,21 @@
 
         private fun Desktop.Builder.updateTaskStates(
             visibleTasks: ArraySet<Int>,
-            minimizedTasks: ArraySet<Int>
+            minimizedTasks: ArraySet<Int>,
+            freeformTasksInZOrder: ArrayList<Int>,
         ): Desktop.Builder {
             clearTasksByTaskId()
+
+            // Handle the case where tasks are not marked as visible but are meant to be visible
+            // after reboot. E.g. User moves out of desktop when there are multiple tasks are
+            // visible, they will be marked as not visible afterwards. This ensures that they are
+            // still persisted as visible.
+            // TODO - b/350476823: Remove this logic once repository holds expanded tasks
+            if (freeformTasksInZOrder.size > visibleTasks.size + minimizedTasks.size &&
+                visibleTasks.isEmpty()
+            ) {
+                visibleTasks.addAll(freeformTasksInZOrder.filterNot { it in minimizedTasks })
+            }
             putAllTasksByTaskId(
                 visibleTasks.associateWith {
                     createDesktopTask(it, state = DesktopTaskState.VISIBLE)
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..d815656
--- /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 android.window.DesktopModeFlags
+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 (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) 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/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/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index f060158..4aeecbe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -30,6 +30,7 @@
 import android.app.TaskInfo;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.view.Surface;
@@ -152,7 +153,6 @@
         return mCurrentAnimator;
     }
 
-    @SuppressWarnings("unchecked")
     /**
      * Construct and return an animator that animates from the {@param startBounds} to the
      * {@param endBounds} with the given {@param direction}. If {@param direction} is type
@@ -171,6 +171,7 @@
      * leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the
      * rotation change.
      */
+    @SuppressWarnings("unchecked")
     @VisibleForTesting
     public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
             Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
@@ -566,7 +567,7 @@
                     }
                     getSurfaceTransactionHelper()
                             .resetScale(tx, leash, getDestinationBounds())
-                            .crop(tx, leash, getDestinationBounds())
+                            .cropAndPosition(tx, leash, getDestinationBounds())
                             .round(tx, leash, true /* applyCornerRadius */)
                             .shadow(tx, leash, shouldApplyShadowRadius());
                     tx.show(leash);
@@ -590,18 +591,50 @@
             // Just for simplicity we'll interpolate between the source rect hint insets and empty
             // insets to calculate the window crop
             final Rect initialSourceValue;
+            final Rect mainWindowFrame = taskInfo.topActivityMainWindowFrame;
+            final boolean hasNonMatchFrame = mainWindowFrame != null;
+            final boolean changeOrientation =
+                    rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270;
+            final Rect baseBounds = new Rect(baseValue);
+            final Rect startBounds = new Rect(startValue);
+            final Rect endBounds = new Rect(endValue);
             if (isOutPipDirection) {
-                initialSourceValue = new Rect(endValue);
+                // TODO(b/356277166): handle rotation change with activity that provides main window
+                //  frame.
+                if (hasNonMatchFrame && !changeOrientation) {
+                    endBounds.set(mainWindowFrame);
+                }
+                initialSourceValue = new Rect(endBounds);
+            } else if (isInPipDirection) {
+                if (hasNonMatchFrame) {
+                    baseBounds.set(mainWindowFrame);
+                    if (startValue.equals(baseValue)) {
+                        // If the start value is at initial state as in PIP animation, also override
+                        // the start bounds with nonMatchParentBounds.
+                        startBounds.set(mainWindowFrame);
+                    }
+                }
+                initialSourceValue = new Rect(baseBounds);
             } else {
-                initialSourceValue = new Rect(baseValue);
+                // Note that we assume the window bounds always match task bounds in PIP mode.
+                initialSourceValue = new Rect(baseBounds);
+            }
+
+            final Point leashOffset;
+            if (isInPipDirection) {
+                leashOffset = new Point(baseValue.left, baseValue.top);
+            } else if (isOutPipDirection) {
+                leashOffset = new Point(endValue.left, endValue.top);
+            } else {
+                leashOffset = new Point(baseValue.left, baseValue.top);
             }
 
             final Rect rotatedEndRect;
             final Rect lastEndRect;
             final Rect initialContainerRect;
-            if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
-                lastEndRect = new Rect(endValue);
-                rotatedEndRect = new Rect(endValue);
+            if (changeOrientation) {
+                lastEndRect = new Rect(endBounds);
+                rotatedEndRect = new Rect(endBounds);
                 // Rotate the end bounds according to the rotation delta because the display will
                 // be rotated to the same orientation.
                 rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta);
@@ -617,9 +650,9 @@
                 // Crop a Rect matches the aspect ratio and pivots at the center point.
                 // This is done for entering case only.
                 if (isInPipDirection(direction)) {
-                    final float aspectRatio = endValue.width() / (float) endValue.height();
+                    final float aspectRatio = endBounds.width() / (float) endBounds.height();
                     adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint(
-                            startValue, aspectRatio));
+                            startBounds, aspectRatio));
                 }
             } else {
                 adjustedSourceRectHint.set(sourceRectHint);
@@ -644,7 +677,7 @@
 
             // construct new Rect instances in case they are recycled
             return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
-                    endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) {
+                    endBounds, new Rect(baseBounds), new Rect(startBounds), new Rect(endBounds)) {
                 private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
                 private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
 
@@ -668,11 +701,22 @@
                     setCurrentValue(bounds);
                     if (inScaleTransition() || adjustedSourceRectHint.isEmpty()) {
                         if (isOutPipDirection) {
-                            getSurfaceTransactionHelper().crop(tx, leash, end)
-                                    .scale(tx, leash, end, bounds);
+                            // Use the bounds relative to the task leash in case the leash does not
+                            // start from (0, 0).
+                            final Rect relativeEndBounds = new Rect(end);
+                            relativeEndBounds.offset(-leashOffset.x, -leashOffset.y);
+                            getSurfaceTransactionHelper()
+                                    .crop(tx, leash, relativeEndBounds)
+                                    .scale(tx, leash, relativeEndBounds, bounds,
+                                            false /* shouldOffset */);
                         } else {
-                            getSurfaceTransactionHelper().crop(tx, leash, base)
-                                    .scale(tx, leash, base, bounds, angle)
+                            // TODO(b/356277166): add support to specify sourceRectHint with
+                            //  non-match parent activity.
+                            // If there's a PIP resize animation, we should offset the bounds to
+                            // (0, 0) since the window bounds should match the leash bounds in PIP
+                            // mode.
+                            getSurfaceTransactionHelper().cropAndPosition(tx, leash, base)
+                                    .scale(tx, leash, base, bounds, angle, inScaleTransition())
                                     .round(tx, leash, base, bounds)
                                     .shadow(tx, leash, shouldApplyShadowRadius());
                         }
@@ -680,7 +724,7 @@
                         final Rect insets = computeInsets(fraction);
                         getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
                                 adjustedSourceRectHint, initialSourceValue, bounds, insets,
-                                isInPipDirection, fraction);
+                                isInPipDirection, fraction, leashOffset);
                         final Rect sourceBounds = new Rect(initialContainerRect);
                         sourceBounds.inset(insets);
                         getSurfaceTransactionHelper()
@@ -733,8 +777,7 @@
                     getSurfaceTransactionHelper()
                             .rotateAndScaleWithCrop(tx, leash, initialContainerRect, bounds,
                                     insets, degree, x, y, isOutPipDirection,
-                                    rotationDelta == ROTATION_270 /* clockwise */);
-                    getSurfaceTransactionHelper()
+                                    rotationDelta == ROTATION_270 /* clockwise */)
                             .round(tx, leash, sourceBounds, bounds)
                             .shadow(tx, leash, shouldApplyShadowRadius());
                     if (!handlePipTransaction(leash, tx, bounds, 1f /* alpha */)) {
@@ -772,7 +815,7 @@
                         tx.setPosition(leash, 0, 0);
                         tx.setWindowCrop(leash, 0, 0);
                     } else {
-                        getSurfaceTransactionHelper().crop(tx, leash, destBounds);
+                        getSurfaceTransactionHelper().cropAndPosition(tx, leash, destBounds);
                     }
                     if (mContentOverlay != null) {
                         clearContentOverlay();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index 3d1994c..b02bd0f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -16,8 +16,10 @@
 
 package com.android.wm.shell.pip;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.view.Choreographer;
@@ -68,52 +70,102 @@
      * Operates the crop (and position) on a given transaction and leash
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
      */
-    public PipSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash,
-            Rect destinationBounds) {
+    public PipSurfaceTransactionHelper cropAndPosition(@NonNull SurfaceControl.Transaction tx,
+            @NonNull SurfaceControl leash, @NonNull Rect destinationBounds) {
         tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
                 .setPosition(leash, destinationBounds.left, destinationBounds.top);
         return this;
     }
 
     /**
+     * Operates {@link SurfaceControl.Transaction#setCrop} on a given transaction and leash.
+     *
+     * @param tx the transaction to  apply
+     * @param leash the leash to crop
+     * @param relativeDestinationBounds the bounds to crop, which is relative to the leash
+     *                                  coordinate
+     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+     */
+    public PipSurfaceTransactionHelper crop(@NonNull SurfaceControl.Transaction tx,
+            @NonNull SurfaceControl leash, @NonNull Rect relativeDestinationBounds) {
+        tx.setCrop(leash, relativeDestinationBounds);
+        return this;
+    }
+
+    /**
      * Operates the scale (setMatrix) on a given transaction and leash
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
      */
     public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
             Rect sourceBounds, Rect destinationBounds) {
         mTmpDestinationRectF.set(destinationBounds);
-        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */);
+        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */,
+                true /* shouldOffset */);
     }
 
     /**
      * Operates the scale (setMatrix) on a given transaction and leash
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
      */
-    public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
-            Rect sourceBounds, RectF destinationBounds) {
-        return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */);
+    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+            @NonNull RectF destinationBounds) {
+        return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */,
+                true /* shouldOffset */);
     }
 
     /**
-     * Operates the scale (setMatrix) on a given transaction and leash
+     * Operates the scale (setMatrix) on a given transaction and leash.
+     *
+     * @param shouldOffset {@code true} to offset the leash to (0, 0)
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
      */
-    public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
-            Rect sourceBounds, Rect destinationBounds, float degrees) {
+    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+            @NonNull Rect destinationBounds, boolean shouldOffset) {
         mTmpDestinationRectF.set(destinationBounds);
-        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, degrees);
+        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, 0 /* degrees */, shouldOffset);
+    }
+
+    /**
+     * Operates the scale (setMatrix) on a given transaction and leash.
+     *
+     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+     */
+    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+            @NonNull Rect destinationBounds, float degrees) {
+        return scale(tx, leash, sourceBounds, destinationBounds, degrees, true /* shouldOffset */);
+    }
+
+    /**
+     * Operates the scale (setMatrix) on a given transaction and leash.
+     *
+     * @param shouldOffset {@code true} to offset the leash to (0, 0)
+     * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+     */
+    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+            @NonNull Rect destinationBounds, float degrees, boolean shouldOffset) {
+        mTmpDestinationRectF.set(destinationBounds);
+        return scale(tx, leash, sourceBounds, mTmpDestinationRectF, degrees, shouldOffset);
     }
 
     /**
      * Operates the scale (setMatrix) on a given transaction and leash, along with a rotation.
+     *
+     * @param shouldOffset {@code true} to offset the leash to (0, 0)
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
      */
-    public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
-            Rect sourceBounds, RectF destinationBounds, float degrees) {
+    public PipSurfaceTransactionHelper scale(@NonNull SurfaceControl.Transaction tx,
+            @NonNull SurfaceControl leash, @NonNull Rect sourceBounds,
+            @NonNull RectF destinationBounds, float degrees, boolean shouldOffset) {
         mTmpSourceRectF.set(sourceBounds);
         // We want the matrix to position the surface relative to the screen coordinates so offset
-        // the source to 0,0
-        mTmpSourceRectF.offsetTo(0, 0);
+        // the source to (0, 0) if {@code shouldOffset} is true.
+        if (shouldOffset) {
+            mTmpSourceRectF.offsetTo(0, 0);
+        }
         mTmpDestinationRectF.set(destinationBounds);
         mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
         mTmpTransform.postRotate(degrees,
@@ -123,17 +175,19 @@
     }
 
     /**
-     * Operates the scale (setMatrix) on a given transaction and leash
+     * Operates the scale (setMatrix) on a given transaction and leash.
+     *
+     * @param leashOffset the offset of the leash bounds relative to the screen coordinate
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
      */
-    public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx,
-            SurfaceControl leash, Rect sourceRectHint,
-            Rect sourceBounds, Rect destinationBounds, Rect insets,
-            boolean isInPipDirection, float fraction) {
+    public PipSurfaceTransactionHelper scaleAndCrop(@NonNull SurfaceControl.Transaction tx,
+            @NonNull SurfaceControl leash, @NonNull Rect sourceRectHint, @NonNull Rect sourceBounds,
+            @NonNull Rect destinationBounds, @NonNull Rect insets, boolean isInPipDirection,
+            float fraction, @NonNull Point leashOffset) {
         mTmpDestinationRect.set(sourceBounds);
         // Similar to {@link #scale}, we want to position the surface relative to the screen
-        // coordinates so offset the bounds to 0,0
-        mTmpDestinationRect.offsetTo(0, 0);
+        // coordinates so offset the bounds relative to the leash.
+        mTmpDestinationRect.offset(-leashOffset.x, -leashOffset.y);
         mTmpDestinationRect.inset(insets);
         // Scale to the bounds no smaller than the destination and offset such that the top/left
         // of the scaled inset source rect aligns with the top/left of the destination bounds
@@ -152,13 +206,13 @@
             scale = Math.max((float) destinationBounds.width() / sourceBounds.width(),
                     (float) destinationBounds.height() / sourceBounds.height());
         }
-        float left = destinationBounds.left - insets.left * scale;
-        float top = destinationBounds.top - insets.top * scale;
+        float left = destinationBounds.left - mTmpDestinationRect.left * scale;
+        float top = destinationBounds.top - mTmpDestinationRect.top * scale;
         if (scale == 1) {
             // Work around the 1 pixel off error by rounding the position down at very beginning.
             // We noticed such error from flicker tests, not visually.
-            left = sourceBounds.left;
-            top = sourceBounds.top;
+            left = leashOffset.x;
+            top = leashOffset.y;
         }
         mTmpTransform.setScale(scale, scale);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b4e0329..c4e63df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -960,7 +960,7 @@
         final SurfaceControl.Transaction boundsChangeTx =
                 mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
-                .crop(boundsChangeTx, mLeash, destinationBounds)
+                .cropAndPosition(boundsChangeTx, mLeash, destinationBounds)
                 .round(boundsChangeTx, mLeash, true /* applyCornerRadius */);
 
         mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
@@ -988,7 +988,7 @@
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
                 .resetScale(tx, mLeash, destinationBounds)
-                .crop(tx, mLeash, destinationBounds)
+                .cropAndPosition(tx, mLeash, destinationBounds)
                 .round(tx, mLeash, isInPip());
         // The animation is finished in the Launcher and here we directly apply the final touch.
         applyEnterPipSyncTransaction(destinationBounds, () -> {
@@ -1525,7 +1525,7 @@
         mPipBoundsState.setBounds(toBounds);
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
-                .crop(tx, mLeash, toBounds)
+                .cropAndPosition(tx, mLeash, toBounds)
                 .round(tx, mLeash, mPipTransitionState.isInPip());
         if (shouldSyncPipTransactionWithMenu()) {
             mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
@@ -1628,7 +1628,7 @@
             Rect destinationBounds) {
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
-                .crop(tx, mLeash, destinationBounds)
+                .cropAndPosition(tx, mLeash, destinationBounds)
                 .resetScale(tx, mLeash, destinationBounds)
                 .round(tx, mLeash, mPipTransitionState.isInPip());
         return tx;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 6da3995..28b91c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -30,7 +30,6 @@
 import static android.view.WindowManager.TRANSIT_PIP;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.view.WindowManager.transitTypeToString;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
@@ -45,6 +44,7 @@
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
+import static com.android.wm.shell.transition.Transitions.transitTypeToString;
 
 import android.annotation.IntDef;
 import android.app.ActivityManager;
@@ -551,7 +551,7 @@
                 }
                 // Reset the scale with bounds change synchronously.
                 if (hasValidLeash) {
-                    mSurfaceTransactionHelper.crop(tx, leash, destinationBounds)
+                    mSurfaceTransactionHelper.cropAndPosition(tx, leash, destinationBounds)
                             .resetScale(tx, leash, destinationBounds)
                             .round(tx, leash, true /* applyCornerRadius */);
                     final Rect appBounds = mPipOrganizer.mAppBounds;
@@ -588,7 +588,8 @@
                     ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                             "%s: Destination bounds were changed during animation", TAG);
                     rotateBounds(finishBounds, displayBounds, mEndFixedRotation, displayRotation);
-                    mSurfaceTransactionHelper.crop(mFinishTransaction, leash, finishBounds);
+                    mSurfaceTransactionHelper.cropAndPosition(mFinishTransaction, leash,
+                            finishBounds);
                 }
             }
             mFinishTransaction = null;
@@ -1068,6 +1069,11 @@
         mPipBoundsState.mayUseCachedLauncherShelfHeight();
         final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
         final Rect currentBounds = pipChange.getStartAbsBounds();
+        // The app bounds should offset relative to the task leash to make the center calculation
+        // correctly.
+        final Rect relativeAppBounds = new Rect(taskInfo.topActivityMainWindowFrame != null
+                ? taskInfo.topActivityMainWindowFrame : currentBounds);
+        relativeAppBounds.offset(-currentBounds.left, -currentBounds.top);
 
         int rotationDelta = deltaRotation(startRotation, endRotation);
         Rect sourceHintRect = mPipOrganizer.takeSwipeSourceRectHint();
@@ -1089,6 +1095,8 @@
         if (taskInfo.pictureInPictureParams != null
                 && taskInfo.pictureInPictureParams.isAutoEnterEnabled()
                 && mPipTransitionState.getInSwipePipToHomeTransition()) {
+            // TODO(b/356277166): add support to swipe PIP to home with
+            //  non-match parent activity.
             handleSwipePipToHomeTransition(startTransaction, finishTransaction, leash,
                     sourceHintRect, destinationBounds, taskInfo);
             return;
@@ -1119,8 +1127,8 @@
                 // TODO(b/272819817): cleanup the null-check and extra logging.
                 final boolean hasTopActivityInfo = taskInfo.topActivityInfo != null;
                 if (hasTopActivityInfo && mFixedRotationState != FIXED_ROTATION_TRANSITION) {
-                    animator.setAppIconContentOverlay(
-                            mContext, currentBounds, destinationBounds, taskInfo.topActivityInfo,
+                    animator.setAppIconContentOverlay(mContext, relativeAppBounds,
+                            destinationBounds, taskInfo.topActivityInfo,
                             mPipBoundsState.getLauncherState().getAppIconSizePx());
                 } else {
                     ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -1149,14 +1157,14 @@
                 animationDuration = 0;
             }
             mSurfaceTransactionHelper
-                    .crop(finishTransaction, leash, destinationBounds)
+                    .cropAndPosition(finishTransaction, leash, destinationBounds)
                     .round(finishTransaction, leash, true /* applyCornerRadius */);
             // Always reset to bounds animation type afterwards.
             setEnterAnimationType(ANIM_TYPE_BOUNDS);
         } else {
             throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
         }
-        mPipOrganizer.setContentOverlay(animator.getContentOverlayLeash(), currentBounds);
+        mPipOrganizer.setContentOverlay(animator.getContentOverlayLeash(), relativeAppBounds);
         animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(animationDuration);
@@ -1340,11 +1348,11 @@
                 "%s: Update pip for unhandled transition, change=%s, destBounds=%s, isInPip=%b",
                 TAG, pipChange, destBounds, isInPip);
         mSurfaceTransactionHelper
-                .crop(startTransaction, leash, destBounds)
+                .cropAndPosition(startTransaction, leash, destBounds)
                 .round(startTransaction, leash, isInPip)
                 .shadow(startTransaction, leash, isInPip);
         mSurfaceTransactionHelper
-                .crop(finishTransaction, leash, destBounds)
+                .cropAndPosition(finishTransaction, leash, destBounds)
                 .round(finishTransaction, leash, isInPip)
                 .shadow(finishTransaction, leash, isInPip);
         // Make sure the PiP keeps invisible if it was faded out. If it needs to fade in, that will
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/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index 7a0e669..d3ae411 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -23,7 +23,6 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_PIP;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManager.transitTypeToString;
 
 import static com.android.wm.shell.common.pip.PipMenuController.ALPHA_NO_CHANGE;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
@@ -35,6 +34,7 @@
 import static com.android.wm.shell.pip.PipTransitionState.UNDEFINED;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
+import static com.android.wm.shell.transition.Transitions.transitTypeToString;
 
 import android.animation.AnimationHandler;
 import android.animation.Animator;
@@ -338,7 +338,7 @@
         final Rect pipBounds = mPipBoundsState.getBounds();
         mSurfaceTransactionHelper
                 .resetScale(startTransaction, pipLeash, pipBounds)
-                .crop(startTransaction, pipLeash, pipBounds)
+                .cropAndPosition(startTransaction, pipLeash, pipBounds)
                 .shadow(startTransaction, pipLeash, false);
 
         final SurfaceControl.Transaction transaction = mTransactionFactory.getTransaction();
@@ -420,7 +420,7 @@
 
         mSurfaceTransactionHelper
                 .resetScale(finishTransaction, leash, pipBounds)
-                .crop(finishTransaction, leash, pipBounds)
+                .cropAndPosition(finishTransaction, leash, pipBounds)
                 .shadow(finishTransaction, leash, false);
 
         final Rect currentBounds = pipChange.getStartAbsBounds();
@@ -443,7 +443,7 @@
                 SurfaceControl.Transaction tx = mTransactionFactory.getTransaction();
                 mSurfaceTransactionHelper
                         .resetScale(tx, leash, pipBounds)
-                        .crop(tx, leash, pipBounds)
+                        .cropAndPosition(tx, leash, pipBounds)
                         .shadow(tx, leash, false);
                 mShellTaskOrganizer.applyTransaction(resizePipWct);
                 tx.apply();
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 740b9af..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
@@ -192,22 +192,7 @@
      * 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);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
index a93ef12..3f9b0c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.pip2.animation;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -27,6 +28,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;
 import com.android.wm.shell.shared.animation.Interpolators;
@@ -34,8 +36,7 @@
 /**
  * Animator that handles bounds animations for exit-via-expanding PIP.
  */
-public class PipExpandAnimator extends ValueAnimator
-        implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
+public class PipExpandAnimator extends ValueAnimator {
     @NonNull
     private final SurfaceControl mLeash;
     private final SurfaceControl.Transaction mStartTransaction;
@@ -58,12 +59,61 @@
     // Bounds updated by the evaluator as animator is running.
     private final Rect mAnimatedRect = new Rect();
 
-    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+    private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
     private final RectEvaluator mRectEvaluator;
     private final RectEvaluator mInsetEvaluator;
     private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
 
+    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 (mFinishTransaction != null) {
+                // finishTransaction might override some state (eg. corner radii) so we want to
+                // manually set the state to the end of the animation
+                mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash,
+                                mSourceRectHint, mBaseBounds, mAnimatedRect, getInsets(1f),
+                                false /* isInPipDirection */, 1f)
+                        .round(mFinishTransaction, mLeash, false /* applyCornerRadius */)
+                        .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */);
+            }
+            if (mAnimationEndCallback != null) {
+                mAnimationEndCallback.run();
+            }
+        }
+    };
+
+    private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener =
+            new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+                    final SurfaceControl.Transaction tx =
+                            mSurfaceControlTransactionFactory.getTransaction();
+                    final float fraction = getAnimatedFraction();
+
+                    // TODO (b/350801661): implement fixed rotation
+                    Rect insets = getInsets(fraction);
+                    mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
+                                    mBaseBounds, mAnimatedRect,
+                                    insets, false /* isInPipDirection */, fraction)
+                            .round(tx, mLeash, false /* applyCornerRadius */)
+                            .shadow(tx, mLeash, false /* applyCornerRadius */);
+                    tx.apply();
+                }
+            };
+
     public PipExpandAnimator(Context context,
             @NonNull SurfaceControl leash,
             SurfaceControl.Transaction startTransaction,
@@ -105,8 +155,8 @@
         setObjectValues(startBounds, endBounds);
         setEvaluator(mRectEvaluator);
         setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        addListener(this);
-        addUpdateListener(this);
+        addListener(mAnimatorListener);
+        addUpdateListener(mAnimatorUpdateListener);
     }
 
     public void setAnimationStartCallback(@NonNull Runnable runnable) {
@@ -117,58 +167,15 @@
         mAnimationEndCallback = runnable;
     }
 
-    @Override
-    public void onAnimationStart(@NonNull Animator animation) {
-        if (mAnimationStartCallback != null) {
-            mAnimationStartCallback.run();
-        }
-        if (mStartTransaction != null) {
-            mStartTransaction.apply();
-        }
-    }
-
-    @Override
-    public void onAnimationEnd(@NonNull Animator animation) {
-        if (mFinishTransaction != null) {
-            // finishTransaction might override some state (eg. corner radii) so we want to
-            // manually set the state to the end of the animation
-            mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint,
-                            mBaseBounds, mAnimatedRect, getInsets(1f),
-                            false /* isInPipDirection */, 1f)
-                    .round(mFinishTransaction, mLeash, false /* applyCornerRadius */)
-                    .shadow(mFinishTransaction, mLeash, false /* applyCornerRadius */);
-        }
-        if (mAnimationEndCallback != null) {
-            mAnimationEndCallback.run();
-        }
-    }
-
-    @Override
-    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
-        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
-        final float fraction = getAnimatedFraction();
-
-        // TODO (b/350801661): implement fixed rotation
-
-        Rect insets = getInsets(fraction);
-        mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
-                        mBaseBounds, mAnimatedRect, insets, false /* isInPipDirection */, fraction)
-                .round(tx, mLeash, false /* applyCornerRadius */)
-                .shadow(tx, mLeash, false /* applyCornerRadius */);
-        tx.apply();
-    }
-
     private Rect getInsets(float fraction) {
         final Rect startInsets = mSourceRectHintInsets;
         final Rect endInsets = mZeroInsets;
         return mInsetEvaluator.evaluate(fraction, startInsets, endInsets);
     }
 
-    // no-ops
-
-    @Override
-    public void onAnimationCancel(@NonNull Animator animation) {}
-
-    @Override
-    public void onAnimationRepeat(@NonNull Animator animation) {}
+    @VisibleForTesting
+    void setSurfaceControlTransactionFactory(
+            @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+        mSurfaceControlTransactionFactory = factory;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index d565776..012dabb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.pip2.animation;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.RectEvaluator;
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -27,13 +28,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
 
 /**
  * Animator that handles any resize related animation for PIP.
  */
-public class PipResizeAnimator extends ValueAnimator
-        implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener{
+public class PipResizeAnimator extends ValueAnimator {
     @NonNull
     private final Context mContext;
     @NonNull
@@ -61,9 +62,47 @@
     private final Rect mAnimatedRect = new Rect();
     private final float mDelta;
 
-    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+    private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
 
+    private final Animator.AnimatorListener mAnimatorListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationStart(Animator animation) {
+            super.onAnimationStart(animation);
+            if (mAnimationStartCallback != null) {
+                mAnimationStartCallback.run();
+            }
+            if (mStartTx != null) {
+                setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta);
+                mStartTx.apply();
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            if (mFinishTx != null) {
+                setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f);
+            }
+            if (mAnimationEndCallback != null) {
+                mAnimationEndCallback.run();
+            }
+        }
+    };
+
+    private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener =
+            new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+                    final SurfaceControl.Transaction tx =
+                            mSurfaceControlTransactionFactory.getTransaction();
+                    final float fraction = getAnimatedFraction();
+                    final float degrees = (1.0f - fraction) * mDelta;
+                    setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees);
+                    tx.apply();
+                }
+            };
+
     public PipResizeAnimator(@NonNull Context context,
             @NonNull SurfaceControl leash,
             @Nullable SurfaceControl.Transaction startTransaction,
@@ -89,8 +128,8 @@
         mRectEvaluator = new RectEvaluator(mAnimatedRect);
 
         setObjectValues(startBounds, endBounds);
-        addListener(this);
-        addUpdateListener(this);
+        addListener(mAnimatorListener);
+        addUpdateListener(mAnimatorUpdateListener);
         setEvaluator(mRectEvaluator);
         setDuration(duration);
     }
@@ -103,26 +142,6 @@
         mAnimationEndCallback = runnable;
     }
 
-    @Override
-    public void onAnimationStart(@NonNull Animator animation) {
-        if (mAnimationStartCallback != null) {
-            mAnimationStartCallback.run();
-        }
-        if (mStartTx != null) {
-            setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta);
-            mStartTx.apply();
-        }
-    }
-
-    @Override
-    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
-        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
-        final float fraction = getAnimatedFraction();
-        final float degrees = (1.0f - fraction) * mDelta;
-        setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees);
-        tx.apply();
-    }
-
     /**
      * Set a proper transform matrix for a leash to move it to given bounds with a certain rotation.
      *
@@ -130,7 +149,7 @@
      * @param targetBounds bounds to which we are scaling the leash.
      * @param degrees degrees of rotation - counter-clockwise is positive by convention.
      */
-    public static void setBoundsAndRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
+    private static void setBoundsAndRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
             Rect baseBounds, Rect targetBounds, float degrees) {
         Matrix transformTensor = new Matrix();
         final float[] mMatrixTmp = new float[9];
@@ -144,19 +163,9 @@
         tx.setMatrix(leash, transformTensor, mMatrixTmp);
     }
 
-    @Override
-    public void onAnimationEnd(@NonNull Animator animation) {
-        if (mFinishTx != null) {
-            setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f);
-        }
-        if (mAnimationEndCallback != null) {
-            mAnimationEndCallback.run();
-        }
+    @VisibleForTesting
+    void setSurfaceControlTransactionFactory(@NonNull
+            PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+        mSurfaceControlTransactionFactory = factory;
     }
-
-    @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/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/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 e90b32c..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
@@ -35,6 +35,7 @@
 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;
@@ -276,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);
@@ -353,10 +358,12 @@
             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();
 
@@ -368,7 +375,7 @@
                 tx.apply();
             });
         }
-        finishInner();
+        finishTransition();
         return true;
     }
 
@@ -393,14 +400,13 @@
 
         final PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
         final float aspectRatio = mPipBoundsAlgorithm.getAspectRatioOrDefault(params);
-
         final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds,
                 endBounds);
-
-        final SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
         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
         // by the Transitions framework to simplify Task opening transitions.
@@ -435,14 +441,16 @@
                     mContext, startBounds, endBounds, pipChange.getTaskInfo().topActivityInfo,
                     mPipBoundsState.getLauncherState().getAppIconSizePx());
         }
-        animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange,
-                pipActivityChange));
+
+        prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange,
+                pipActivityChange);
+        animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange));
         animator.setAnimationEndCallback(() -> {
             if (animator.getContentOverlayLeash() != null) {
                 startOverlayFadeoutAnimation(animator.getContentOverlayLeash(),
                         animator::clearAppIconOverlay);
             }
-            finishInner();
+            finishTransition();
         });
         animator.start();
         return true;
@@ -512,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;
     }
@@ -569,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;
     }
@@ -698,33 +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.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/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
index 245829e..371bdd5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -45,4 +45,7 @@
 
     /** A task has moved to front. */
     oneway void onTaskMovedToFront(in RunningTaskInfo taskInfo);
+
+    /** A task info has changed. */
+    oneway void onTaskInfoChanged(in RunningTaskInfo taskInfo);
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 6086801..faa2015 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -47,7 +47,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
 import com.android.wm.shell.common.ExternalInterfaceBinder;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
@@ -289,6 +288,11 @@
     }
 
     @Override
+    public void onTaskChangedThroughTransition(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
+        notifyTaskInfoChanged(taskInfo);
+    }
+
+    @Override
     public void onTaskMovedToFrontThroughTransition(
             ActivityManager.RunningTaskInfo runningTaskInfo) {
         notifyTaskMovedToFront(runningTaskInfo);
@@ -355,6 +359,19 @@
         }
     }
 
+    private void notifyTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        if (mListener == null
+                || !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
+                || taskInfo.realActivity == null) {
+            return;
+        }
+        try {
+            mListener.onTaskInfoChanged(taskInfo);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed call onTaskInfoChanged", e);
+        }
+    }
+
     private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
         if (mListener == null
                 || !DesktopModeFlags.ENABLE_TASK_STACK_OBSERVER_IN_SHELL.isTrue()
@@ -426,7 +443,7 @@
                 // If task has their app bounds set to null which happens after reboot, set the
                 // app bounds to persisted lastFullscreenBounds. Also set the position in parent
                 // to the top left of the bounds.
-                if (Flags.enableDesktopWindowingPersistence()
+                if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()
                         && taskInfo.configuration.windowConfiguration.getAppBounds() == null) {
                     taskInfo.configuration.windowConfiguration.setAppBounds(
                             taskInfo.lastNonFullscreenBounds);
@@ -636,6 +653,11 @@
             public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
                 mListener.call(l -> l.onTaskMovedToFront(taskInfo));
             }
+
+            @Override
+            public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+                mListener.call(l -> l.onTaskInfoChanged(taskInfo));
+            }
         };
 
         public IRecentTasksImpl(RecentTasksController controller) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
index 1af99f9..d28a462 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
@@ -20,8 +20,9 @@
 import android.os.IBinder
 import android.util.ArrayMap
 import android.view.SurfaceControl
-import android.window.TransitionInfo
+import android.view.WindowManager.TRANSIT_CHANGE
 import android.window.DesktopModeFlags
+import android.window.TransitionInfo
 import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
@@ -69,8 +70,10 @@
                 // Find the first task that is opening, this should be the one at the front after
                 // the transition
                 if (TransitionUtil.isOpeningType(change.mode)) {
-                    notifyTaskStackTransitionObserverListeners(taskInfo)
+                    notifyOnTaskMovedToFront(taskInfo)
                     break
+                } else if (change.mode == TRANSIT_CHANGE) {
+                    notifyOnTaskChanged(taskInfo)
                 }
             }
         }
@@ -95,15 +98,23 @@
         taskStackTransitionObserverListeners.remove(taskStackTransitionObserverListener)
     }
 
-    private fun notifyTaskStackTransitionObserverListeners(taskInfo: RunningTaskInfo) {
+    private fun notifyOnTaskMovedToFront(taskInfo: RunningTaskInfo) {
         taskStackTransitionObserverListeners.forEach { (listener, executor) ->
             executor.execute { listener.onTaskMovedToFrontThroughTransition(taskInfo) }
         }
     }
 
+    private fun notifyOnTaskChanged(taskInfo: RunningTaskInfo) {
+        taskStackTransitionObserverListeners.forEach { (listener, executor) ->
+            executor.execute { listener.onTaskChangedThroughTransition(taskInfo) }
+        }
+    }
+
     /** Listener to use to get updates regarding task stack from this observer */
     interface TaskStackTransitionObserverListener {
         /** Called when a task is moved to front. */
         fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {}
+        /** Called when a task info has changed. */
+        fun onTaskChangedThroughTransition(taskInfo: RunningTaskInfo) {}
     }
 }
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..cc0e1df 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
@@ -30,7 +30,6 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.view.WindowManager.transitTypeToString;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
 
@@ -73,6 +72,7 @@
 import static com.android.wm.shell.transition.MixedTransitionHelper.getPipReplacingChange;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
+import static com.android.wm.shell.transition.Transitions.transitTypeToString;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -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/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/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index a27c14bd..4feb475 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -24,7 +24,6 @@
 import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.view.WindowManager.transitTypeToString;
 import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -34,6 +33,7 @@
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+import static com.android.wm.shell.transition.Transitions.transitTypeToString;
 
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 7c9cd08..1d456ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -29,7 +29,6 @@
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.view.WindowManager.fixScale;
-import static android.view.WindowManager.transitTypeToString;
 import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
@@ -725,7 +724,7 @@
             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
         info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
-                info.getDebugId(), transitionToken, info);
+                info.getDebugId(), transitionToken, info.toString("    " /* prefix */));
         int activeIdx = findByToken(mPendingTransitions, transitionToken);
         if (activeIdx < 0) {
             final ActiveTransition existing = mKnownTransitions.get(transitionToken);
@@ -1847,6 +1846,40 @@
         }
     }
 
+    /**
+     * Like WindowManager#transitTypeToString(), but also covers known custom transition types as
+     * well.
+     */
+    public static String transitTypeToString(int transitType) {
+        if (transitType < TRANSIT_FIRST_CUSTOM) {
+            return WindowManager.transitTypeToString(transitType);
+        }
+
+        String typeStr = switch (transitType) {
+            case TRANSIT_EXIT_PIP -> "EXIT_PIP";
+            case TRANSIT_EXIT_PIP_TO_SPLIT -> "EXIT_PIP_TO_SPLIT";
+            case TRANSIT_REMOVE_PIP -> "REMOVE_PIP";
+            case TRANSIT_SPLIT_SCREEN_PAIR_OPEN -> "SPLIT_SCREEN_PAIR_OPEN";
+            case TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE -> "SPLIT_SCREEN_OPEN_TO_SIDE";
+            case TRANSIT_SPLIT_DISMISS_SNAP -> "SPLIT_DISMISS_SNAP";
+            case TRANSIT_SPLIT_DISMISS -> "SPLIT_DISMISS";
+            case TRANSIT_MAXIMIZE -> "MAXIMIZE";
+            case TRANSIT_RESTORE_FROM_MAXIMIZE -> "RESTORE_FROM_MAXIMIZE";
+            case TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP -> "DESKTOP_MODE_START_DRAG_TO_DESKTOP";
+            case TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP -> "DESKTOP_MODE_END_DRAG_TO_DESKTOP";
+            case TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP ->
+                    "DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP";
+            case TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE -> "DESKTOP_MODE_TOGGLE_RESIZE";
+            case TRANSIT_RESIZE_PIP -> "RESIZE_PIP";
+            case TRANSIT_TASK_FRAGMENT_DRAG_RESIZE -> "TASK_FRAGMENT_DRAG_RESIZE";
+            case TRANSIT_SPLIT_PASSTHROUGH -> "SPLIT_PASSTHROUGH";
+            case TRANSIT_CLEANUP_PIP_EXIT -> "CLEANUP_PIP_EXIT";
+            case TRANSIT_MINIMIZE -> "MINIMIZE";
+            default -> "";
+        };
+        return typeStr + "(FIRST_CUSTOM+" + (transitType - TRANSIT_FIRST_CUSTOM) + ")";
+    }
+
     private static boolean getShellTransitEnabled() {
         try {
             if (AppGlobals.getPackageManager().hasSystemFeature(
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 3946b61..c954673 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
@@ -180,12 +180,13 @@
         // all other cases, it is expected that the transition handler positions and crops the task
         // in order to allow the handler time to animate before the task before the final
         // position and crop are set.
-        final boolean shouldSetTaskPositionAndCrop = mTaskDragResizer.isResizingOrAnimating();
+        final boolean shouldSetTaskVisibilityPositionAndCrop =
+                mTaskDragResizer.isResizingOrAnimating();
         // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
         // synced with the buffer transaction (that draws the View). Both will be shown on screen
         // at the same, whereas applying them independently causes flickering. See b/270202228.
         relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */,
-                shouldSetTaskPositionAndCrop, hasGlobalFocus);
+                shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
     }
 
     @VisibleForTesting
@@ -193,7 +194,7 @@
             RelayoutParams relayoutParams,
             ActivityManager.RunningTaskInfo taskInfo,
             boolean applyStartTransactionOnDraw,
-            boolean setTaskCropAndPosition,
+            boolean shouldSetTaskVisibilityPositionAndCrop,
             boolean isStatusBarVisible,
             boolean isKeyguardVisibleAndOccluded,
             InsetsState displayInsetsState,
@@ -206,7 +207,7 @@
                 ? R.dimen.freeform_decor_shadow_focused_thickness
                 : R.dimen.freeform_decor_shadow_unfocused_thickness;
         relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
-        relayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition;
+        relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
         relayoutParams.mIsCaptionVisible = taskInfo.isFreeform()
                 || (isStatusBarVisible && !isKeyguardVisibleAndOccluded);
 
@@ -234,7 +235,7 @@
     @SuppressLint("MissingPermission")
     void relayout(RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
             boolean hasGlobalFocus) {
         final boolean isFreeform =
                 taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -246,7 +247,8 @@
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
         updateRelayoutParams(mRelayoutParams, taskInfo, applyStartTransactionOnDraw,
-                setTaskCropAndPosition, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded,
+                shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible,
+                mIsKeyguardVisibleAndOccluded,
                 mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus);
 
         relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
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 a3324cc6..67775f7 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
@@ -113,6 +113,7 @@
 import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
 import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
 import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
+import com.android.wm.shell.desktopmode.education.AppToWebEducationController;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.shared.FocusTransitionListener;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
@@ -130,7 +131,6 @@
 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;
@@ -177,11 +177,11 @@
     private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
     private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter;
     private final AppHandleEducationController mAppHandleEducationController;
+    private final AppToWebEducationController mAppToWebEducationController;
     private final AppHeaderViewHolder.Factory mAppHeaderViewHolderFactory;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
-    private DesktopStatusBarInputLayerSupplier mStatusBarInputLayerSupplier;
 
     private final ExclusionRegionListener mExclusionRegionListener =
             new ExclusionRegionListenerImpl();
@@ -251,6 +251,7 @@
             MultiInstanceHelper multiInstanceHelper,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             AppHandleEducationController appHandleEducationController,
+            AppToWebEducationController appToWebEducationController,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
             FocusTransitionObserver focusTransitionObserver,
@@ -284,6 +285,7 @@
                 interactionJankMonitor,
                 desktopTasksLimiter,
                 appHandleEducationController,
+                appToWebEducationController,
                 windowDecorCaptionHandleRepository,
                 activityOrientationChangeHandler,
                 new TaskPositionerFactory(),
@@ -321,6 +323,7 @@
             InteractionJankMonitor interactionJankMonitor,
             Optional<DesktopTasksLimiter> desktopTasksLimiter,
             AppHandleEducationController appHandleEducationController,
+            AppToWebEducationController appToWebEducationController,
             WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
             Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
             TaskPositionerFactory taskPositionerFactory,
@@ -356,6 +359,7 @@
         mInteractionJankMonitor = interactionJankMonitor;
         mDesktopTasksLimiter = desktopTasksLimiter;
         mAppHandleEducationController = appHandleEducationController;
+        mAppToWebEducationController = appToWebEducationController;
         mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository;
         mActivityOrientationChangeHandler = activityOrientationChangeHandler;
         mAssistContentRequester = assistContentRequester;
@@ -420,11 +424,7 @@
                         return Unit.INSTANCE;
                     });
         }
-        if (Flags.enableHandleInputFix()) {
-            mStatusBarInputLayerSupplier =
-                    new DesktopStatusBarInputLayerSupplier(mContext, mMainHandler);
-            mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
-        }
+        mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
     }
 
     @Override
@@ -480,7 +480,6 @@
             removeTaskFromEventReceiver(oldTaskInfo.displayId);
             incrementEventReceiverTasks(taskInfo.displayId);
         }
-        decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
         decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
         mActivityOrientationChangeHandler.ifPresent(handler ->
                 handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
@@ -519,7 +518,6 @@
         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));
@@ -673,7 +671,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.detachStatusBarInputLayer();
+        decoration.disposeStatusBarInputLayer();
         mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
     }
 
@@ -1314,8 +1312,8 @@
                         // should not be receiving any input.
                         if (resultType == TO_SPLIT_LEFT_INDICATOR
                                 || resultType == TO_SPLIT_RIGHT_INDICATOR) {
-                            relevantDecor.detachStatusBarInputLayer();
-                            // We should also detach the other split task's input layer if
+                            relevantDecor.disposeStatusBarInputLayer();
+                            // We should also dispose the other split task's input layer if
                             // applicable.
                             final int splitPosition = mSplitScreenController
                                     .getSplitPosition(relevantDecor.mTaskInfo.taskId);
@@ -1328,7 +1326,7 @@
                                         mSplitScreenController.getTaskInfo(oppositePosition);
                                 if (oppositeTaskInfo != null) {
                                     mWindowDecorByTaskId.get(oppositeTaskInfo.taskId)
-                                            .detachStatusBarInputLayer();
+                                            .disposeStatusBarInputLayer();
                                 }
                             }
                         }
@@ -1578,7 +1576,6 @@
                 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));
@@ -1587,18 +1584,6 @@
         }
     }
 
-    /** 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
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 dc27cfe..af7cd05 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
@@ -106,7 +106,6 @@
 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;
@@ -206,7 +205,6 @@
     private final MultiInstanceHelper mMultiInstanceHelper;
     private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
     private final DesktopRepository mDesktopRepository;
-    private AdditionalSystemViewContainer mStatusBarInputLayer;
 
     DesktopModeWindowDecoration(
             Context context,
@@ -392,18 +390,25 @@
     @Override
     void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) {
         final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
-        // The crop and position of the task should only be set when a task is fluid resizing. In
-        // all other cases, it is expected that the transition handler positions and crops the task
-        // in order to allow the handler time to animate before the task before the final
-        // position and crop are set.
-        final boolean shouldSetTaskPositionAndCrop = !DesktopModeStatus.isVeiledResizeEnabled()
-                && mTaskDragResizer.isResizingOrAnimating();
+        // The visibility, crop and position of the task should only be set when a task is
+        // fluid resizing. In all other cases, it is expected that the transition handler sets
+        // those task properties to allow the handler time to animate with full control of the task
+        // leash. In general, allowing the window decoration to set any of these is likely to cause
+        // incorrect frames and flickering because relayouts from TaskListener#onTaskInfoChanged
+        // aren't synchronized with shell transition callbacks, so if they come too early it
+        // might show/hide or crop the task at a bad time.
+        // Fluid resizing is exempt from this because it intentionally doesn't use shell
+        // transitions to resize the task, so onTaskInfoChanged relayouts is the only way to make
+        // sure the crop is set correctly.
+        final boolean shouldSetTaskVisibilityPositionAndCrop =
+                !DesktopModeStatus.isVeiledResizeEnabled()
+                        && mTaskDragResizer.isResizingOrAnimating();
         // For headers only (i.e. in freeform): use |applyStartTransactionOnDraw| so that the
         // transaction (that applies task crop) is synced with the buffer transaction (that draws
         // the View). Both will be shown on screen at the same, whereas applying them independently
         // causes flickering. See b/270202228.
         final boolean applyTransactionOnDraw = taskInfo.isFreeform();
-        relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop,
+        relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop,
                 hasGlobalFocus);
         if (!applyTransactionOnDraw) {
             t.apply();
@@ -430,19 +435,19 @@
 
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
             boolean hasGlobalFocus) {
         Trace.beginSection("DesktopModeWindowDecoration#relayout");
         if (taskInfo.isFreeform()) {
             // The Task is in Freeform mode -> show its header in sync since it's an integral part
             // of the window itself - a delayed header might cause bad UX.
             relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw,
-                    shouldSetTaskPositionAndCrop, hasGlobalFocus);
+                    shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
         } else {
             // The Task is outside Freeform mode -> allow the handle view to be delayed since the
             // handle is just a small addition to the window.
             relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw,
-                    shouldSetTaskPositionAndCrop, hasGlobalFocus);
+                    shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
         }
         Trace.endSection();
     }
@@ -450,12 +455,12 @@
     /** Run the whole relayout phase immediately without delay. */
     private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
             boolean hasGlobalFocus) {
         // Clear the current ViewHost runnable as we will update the ViewHost here
         clearCurrentViewHostRunnable();
         updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw,
-                shouldSetTaskPositionAndCrop, hasGlobalFocus);
+                shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus);
         if (mResult.mRootView != null) {
             updateViewHost(mRelayoutParams, startT, mResult);
         }
@@ -477,7 +482,7 @@
      */
     private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
             boolean hasGlobalFocus) {
         if (applyStartTransactionOnDraw) {
             throw new IllegalArgumentException(
@@ -486,7 +491,7 @@
         // Clear the current ViewHost runnable as we will update the ViewHost here
         clearCurrentViewHostRunnable();
         updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT,
-                false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop,
+                false /* applyStartTransactionOnDraw */, shouldSetTaskVisibilityPositionAndCrop,
                 hasGlobalFocus);
         if (mResult.mRootView == null) {
             // This means something blocks the window decor from showing, e.g. the task is hidden.
@@ -501,10 +506,9 @@
     @SuppressLint("MissingPermission")
     private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
-            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop,
             boolean hasGlobalFocus) {
         Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
-
         if (Flags.enableDesktopWindowingAppToWeb()) {
             setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp);
         }
@@ -526,9 +530,9 @@
         final boolean inFullImmersive = mDesktopRepository
                 .isTaskInFullImmersiveState(taskInfo.taskId);
         updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
-                shouldSetTaskPositionAndCrop, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded,
-                inFullImmersive, mDisplayController.getInsetsState(taskInfo.displayId),
-                hasGlobalFocus);
+                shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible,
+                mIsKeyguardVisibleAndOccluded, inFullImmersive,
+                mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus);
 
         final WindowDecorLinearLayout oldRootView = mResult.mRootView;
         final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
@@ -546,17 +550,17 @@
         if (mResult.mRootView == null) {
             // This means something blocks the window decor from showing, e.g. the task is hidden.
             // Nothing is set up in this case including the decoration surface.
-            if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
+            if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
                 notifyNoCaptionHandle();
             }
             mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
-            detachStatusBarInputLayer();
+            disposeStatusBarInputLayer();
             Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
             return;
         }
 
         if (oldRootView != mResult.mRootView) {
-            detachStatusBarInputLayer();
+            disposeStatusBarInputLayer();
             mWindowDecorViewHolder = createViewHolder();
         }
         Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
@@ -565,7 +569,7 @@
         if (isAppHandle(mWindowDecorViewHolder)) {
             position.set(determineHandlePosition());
         }
-        if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
+        if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
             notifyCaptionStateChanged();
         }
 
@@ -574,9 +578,6 @@
                     mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
                     isCaptionVisible()
             ));
-            if (mStatusBarInputLayer != null) {
-                asAppHandle(mWindowDecorViewHolder).bindStatusBarInputLayer(mStatusBarInputLayer);
-            }
         } else {
             mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
                     mTaskInfo,
@@ -708,7 +709,7 @@
 
     private void notifyCaptionStateChanged() {
         // TODO: b/366159408 - Ensure bounds sent with notification account for RTL mode.
-        if (!canEnterDesktopMode(mContext) || !Flags.enableDesktopWindowingAppHandleEducation()) {
+        if (!canEnterDesktopMode(mContext) || !isEducationEnabled()) {
             return;
         }
         if (!isCaptionVisible()) {
@@ -717,7 +718,7 @@
             // App handle is visible since `mWindowDecorViewHolder` is of type
             // [AppHandleViewHolder].
             final CaptionState captionState = new CaptionState.AppHandle(mTaskInfo,
-                    isHandleMenuActive(), getCurrentAppHandleBounds());
+                    isHandleMenuActive(), getCurrentAppHandleBounds(), isCapturedLinkAvailable());
             mWindowDecorCaptionHandleRepository.notifyCaptionChanged(captionState);
         } else {
             // App header is visible since `mWindowDecorViewHolder` is of type
@@ -730,8 +731,12 @@
         }
     }
 
+    private boolean isCapturedLinkAvailable() {
+        return mCapturedLink != null && !mCapturedLink.mExpired;
+    }
+
     private void notifyNoCaptionHandle() {
-        if (!canEnterDesktopMode(mContext) || !Flags.enableDesktopWindowingAppHandleEducation()) {
+        if (!canEnterDesktopMode(mContext) || !isEducationEnabled()) {
             return;
         }
         mWindowDecorCaptionHandleRepository.notifyCaptionChanged(
@@ -758,7 +763,8 @@
         final CaptionState captionState = new CaptionState.AppHeader(
                 mTaskInfo,
                 isHandleMenuActive(),
-                appChipGlobalPosition);
+                appChipGlobalPosition,
+                isCapturedLinkAvailable());
 
         mWindowDecorCaptionHandleRepository.notifyCaptionChanged(captionState);
     }
@@ -790,15 +796,15 @@
     }
 
     /**
-     * Detach the status bar input layer from this decoration. Intended to be
+     * Dispose of the view used to forward inputs in status bar region. Intended to be
      * used any time handle is no longer visible.
      */
-    void detachStatusBarInputLayer() {
+    void disposeStatusBarInputLayer() {
         if (!isAppHandle(mWindowDecorViewHolder)
                 || !Flags.enableHandleInputFix()) {
             return;
         }
-        asAppHandle(mWindowDecorViewHolder).detachStatusBarInputLayer();
+        asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
     }
 
     private WindowDecorationViewHolder createViewHolder() {
@@ -857,7 +863,7 @@
             Context context,
             ActivityManager.RunningTaskInfo taskInfo,
             boolean applyStartTransactionOnDraw,
-            boolean shouldSetTaskPositionAndCrop,
+            boolean shouldSetTaskVisibilityPositionAndCrop,
             boolean isStatusBarVisible,
             boolean isKeyguardVisibleAndOccluded,
             boolean inFullImmersiveMode,
@@ -953,7 +959,7 @@
                     : R.dimen.freeform_decor_shadow_unfocused_thickness;
         }
         relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
-        relayoutParams.mSetTaskPositionAndCrop = shouldSetTaskPositionAndCrop;
+        relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
 
         // The configuration used to layout the window decoration. A copy is made instead of using
         // the original reference so that the configuration isn't mutated on config changes and
@@ -1094,13 +1100,13 @@
             if (mAppIconBitmap != null && mAppName != null) {
                 return;
             }
-            final ComponentName baseActivity = mTaskInfo.baseActivity;
-            if (baseActivity == null) {
-                Slog.e(TAG, "Base activity component not found in task");
+            if (mTaskInfo.baseIntent == null) {
+                Slog.e(TAG, "Base intent not found in task");
                 return;
             }
             final PackageManager pm = mUserContext.getPackageManager();
-            final ActivityInfo activityInfo = pm.getActivityInfo(baseActivity, 0 /* flags */);
+            final ActivityInfo activityInfo =
+                    pm.getActivityInfo(mTaskInfo.baseIntent.getComponent(), 0 /* flags */);
             final IconProvider provider = new IconProvider(mContext);
             final Drawable appIconDrawable = provider.getIcon(activityInfo);
             final Drawable badgedAppIconDrawable = pm.getUserBadgedIcon(appIconDrawable,
@@ -1387,6 +1393,9 @@
                 /* openInBrowserClickListener= */ (intent) -> {
                     mOpenInBrowserClickListener.accept(intent);
                     onCapturedLinkExpired();
+                    if (Flags.enableDesktopWindowingAppToWebEducation()) {
+                        mWindowDecorCaptionHandleRepository.onAppToWebUsage();
+                    }
                     return Unit.INSTANCE;
                 },
                 /* onOpenByDefaultClickListener= */ () -> {
@@ -1405,7 +1414,7 @@
                 },
                 /* forceShowSystemBars= */ inDesktopImmersive
         );
-        if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
+        if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
             notifyCaptionStateChanged();
         }
         mMinimumInstancesFound = false;
@@ -1452,7 +1461,7 @@
 
     void closeManageWindowsMenu() {
         if (mManageWindowsMenu != null) {
-            mManageWindowsMenu.close();
+            mManageWindowsMenu.animateClose();
         }
         mManageWindowsMenu = null;
     }
@@ -1476,7 +1485,7 @@
         mWindowDecorViewHolder.onHandleMenuClosed();
         mHandleMenu.close();
         mHandleMenu = null;
-        if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
+        if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
             notifyCaptionStateChanged();
         }
     }
@@ -1631,6 +1640,12 @@
                 && v.getTop() <= y && v.getBottom() >= y;
     }
 
+    /** Returns true if at least one education flag is enabled. */
+    private boolean isEducationEnabled() {
+        return Flags.enableDesktopWindowingAppHandleEducation()
+                || Flags.enableDesktopWindowingAppToWebEducation();
+    }
+
     @Override
     public void close() {
         closeDragResizeListener();
@@ -1638,9 +1653,9 @@
         closeManageWindowsMenu();
         mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
         disposeResizeVeil();
-        detachStatusBarInputLayer();
+        disposeStatusBarInputLayer();
         clearCurrentViewHostRunnable();
-        if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
+        if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
             notifyNoCaptionHandle();
         }
         super.close();
@@ -1755,16 +1770,6 @@
                 + "}";
     }
 
-    /**
-     * 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
deleted file mode 100644
index 025bb403..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt
+++ /dev/null
@@ -1,117 +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.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.INVISIBLE
-        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/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index f97dfb89..b016c75 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
@@ -249,7 +249,9 @@
 
         if (!mTaskInfo.isVisible) {
             releaseViews(wct);
-            finishT.hide(mTaskSurface);
+            if (params.mSetTaskVisibilityPositionAndCrop) {
+                finishT.hide(mTaskSurface);
+            }
             return;
         }
 
@@ -422,7 +424,7 @@
 
     private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) {
-        if (params.mSetTaskPositionAndCrop) {
+        if (params.mSetTaskVisibilityPositionAndCrop) {
             final Point taskPosition = mTaskInfo.positionInParent;
             startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
             finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
@@ -437,9 +439,13 @@
             shadowRadius =
                     loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId);
         }
-        startT.setShadowRadius(mTaskSurface, shadowRadius).show(mTaskSurface);
+        startT.setShadowRadius(mTaskSurface, shadowRadius);
         finishT.setShadowRadius(mTaskSurface, shadowRadius);
 
+        if (params.mSetTaskVisibilityPositionAndCrop) {
+            startT.show(mTaskSurface);
+        }
+
         if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
             if (!DesktopModeStatus.isVeiledResizeEnabled()) {
                 // When fluid resize is enabled, add a background to freeform tasks
@@ -758,7 +764,7 @@
         Configuration mWindowDecorConfig;
 
         boolean mApplyStartTransactionOnDraw;
-        boolean mSetTaskPositionAndCrop;
+        boolean mSetTaskVisibilityPositionAndCrop;
         boolean mHasGlobalFocus;
 
         void reset() {
@@ -777,7 +783,7 @@
             mIsCaptionVisible = false;
 
             mApplyStartTransactionOnDraw = false;
-            mSetTaskPositionAndCrop = false;
+            mSetTaskVisibilityPositionAndCrop = false;
             mWindowDecorConfig = null;
             mHasGlobalFocus = false;
         }
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 1451f36..8b6aaaf 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,11 +33,27 @@
  */
 class AdditionalSystemViewContainer(
     private val windowManagerWrapper: WindowManagerWrapper,
-    override val view: View,
-    val lp: LayoutParams
+    taskId: Int,
+    x: Int,
+    y: Int,
+    width: Int,
+    height: Int,
+    flags: Int,
+    @WindowInsets.Type.InsetsType forciblyShownTypes: Int = 0,
+    override val view: View
 ) : 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,
@@ -50,30 +66,15 @@
         @LayoutRes layoutId: Int
     ) : this(
         windowManagerWrapper = windowManagerWrapper,
-        view = LayoutInflater.from(context).inflate(layoutId, null /* parent */),
-        lp = createLayoutParams(x, y, width, height, flags, taskId)
+        taskId = taskId,
+        x = x,
+        y = y,
+        width = width,
+        height = height,
+        flags = flags,
+        view = LayoutInflater.from(context).inflate(layoutId, null /* parent */)
     )
 
-    /** 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,
@@ -85,7 +86,12 @@
         flags: Int
     ) : this(
         windowManagerWrapper = windowManagerWrapper,
-        lp = createLayoutParams(x, y, width, height, flags, taskId),
+        taskId = taskId,
+        x = x,
+        y = y,
+        width = width,
+        height = height,
+        flags = flags,
         view = View(context)
     )
 
@@ -98,7 +104,7 @@
     }
 
     override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) {
-        lp.apply {
+        val lp = (view.layoutParams as WindowManager.LayoutParams).apply {
             this.x = x.toInt()
             this.y = y.toInt()
         }
@@ -118,29 +124,13 @@
         ): AdditionalSystemViewContainer =
             AdditionalSystemViewContainer(
                 windowManagerWrapper = windowManagerWrapper,
-                view = view,
-                lp = createLayoutParams(x, y, width, height, flags, taskId)
+                taskId = taskId,
+                x = x,
+                y = y,
+                width = width,
+                height = height,
+                flags = flags,
+                view = view
             )
     }
-    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/education/DesktopWindowingEducationPromoController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationPromoController.kt
new file mode 100644
index 0000000..b3489a4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationPromoController.kt
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor.education
+
+import android.annotation.ColorInt
+import android.annotation.DimenRes
+import android.annotation.LayoutRes
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Point
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.WindowManager
+import android.widget.LinearLayout
+import android.widget.TextView
+import android.window.DisplayAreaInfo
+import android.window.WindowContainerTransaction
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.wm.shell.R
+import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
+
+/**
+ * Controls the lifecycle of an education promo, including showing and hiding it.
+ */
+class DesktopWindowingEducationPromoController(
+    private val context: Context,
+    private val additionalSystemViewContainerFactory: AdditionalSystemViewContainer.Factory,
+    private val displayController: DisplayController,
+) : OnDisplayChangingListener {
+    private var educationView: View? = null
+    private var animator: PhysicsAnimator<View>? = null
+    private val springConfig by lazy {
+        PhysicsAnimator.SpringConfig(
+            SpringForce.STIFFNESS_MEDIUM,
+            SpringForce.DAMPING_RATIO_LOW_BOUNCY
+        )
+    }
+    private var popupWindow: AdditionalSystemViewContainer? = null
+
+    override fun onDisplayChange(
+        displayId: Int,
+        fromRotation: Int,
+        toRotation: Int,
+        newDisplayAreaInfo: DisplayAreaInfo?,
+        t: WindowContainerTransaction?
+    ) {
+        // Exit if the rotation hasn't changed or is changed by 180 degrees. [fromRotation] and
+        // [toRotation] can be one of the [@Surface.Rotation] values.
+        if ((fromRotation % 2 == toRotation % 2)) return
+        hideEducation()
+    }
+
+    /**
+     * Shows education promo.
+     *
+     * @param viewConfig features of the education.
+     * @param taskId is used in the title of popup window created for the education view.
+     */
+    fun showEducation(
+        viewConfig: EducationViewConfig,
+        taskId: Int
+    ) {
+        hideEducation()
+        educationView = createEducationView(viewConfig, taskId)
+        animator = createAnimator()
+        animateShowEducationTransition()
+        displayController.addDisplayChangingController(this)
+    }
+
+    /** Hide the current education view if visible */
+    private fun hideEducation() = animateHideEducationTransition { cleanUp() }
+
+    /** Create education view by inflating layout provided. */
+    private fun createEducationView(
+        viewConfig: EducationViewConfig,
+        taskId: Int
+    ): View {
+        val educationView =
+            LayoutInflater.from(context)
+                .inflate(
+                    viewConfig.viewLayout, /* root= */ null, /* attachToRoot= */ false)
+                .apply {
+                    alpha = 0f
+                    scaleX = 0f
+                    scaleY = 0f
+
+                    requireViewById<TextView>(R.id.education_text).apply {
+                        text = viewConfig.educationText
+                    }
+                    setOnTouchListener { _, motionEvent ->
+                        if (motionEvent.action == MotionEvent.ACTION_OUTSIDE) {
+                            hideEducation()
+                            true
+                        } else {
+                            false
+                        }
+                    }
+                    setOnClickListener {
+                        hideEducation()
+                    }
+                    setEducationColorScheme(viewConfig.educationColorScheme)
+                }
+
+        createEducationPopupWindow(
+            taskId,
+            viewConfig.viewGlobalCoordinates,
+            loadDimensionPixelSize(viewConfig.widthId),
+            loadDimensionPixelSize(viewConfig.heightId),
+            educationView = educationView)
+
+        return educationView
+    }
+
+    /** Create animator for education transitions */
+    private fun createAnimator(): PhysicsAnimator<View>? =
+        educationView?.let {
+            PhysicsAnimator.getInstance(it).apply { setDefaultSpringConfig(springConfig) }
+        }
+
+    /** Animate show transition for the education view */
+    private fun animateShowEducationTransition() {
+        animator
+            ?.spring(DynamicAnimation.ALPHA, 1f)
+            ?.spring(DynamicAnimation.SCALE_X, 1f)
+            ?.spring(DynamicAnimation.SCALE_Y, 1f)
+            ?.start()
+    }
+
+    /** Animate hide transition for the education view */
+    private fun animateHideEducationTransition(endActions: () -> Unit) {
+        animator
+            ?.spring(DynamicAnimation.ALPHA, 0f)
+            ?.spring(DynamicAnimation.SCALE_X, 0f)
+            ?.spring(DynamicAnimation.SCALE_Y, 0f)
+            ?.start()
+        endActions()
+    }
+
+    /** Remove education promo and clean up all relative properties */
+    private fun cleanUp() {
+        educationView = null
+        animator = null
+        popupWindow?.releaseView()
+        popupWindow = null
+        displayController.removeDisplayChangingController(this)
+    }
+
+    private fun createEducationPopupWindow(
+        taskId: Int,
+        educationViewGlobalCoordinates: Point,
+        width: Int,
+        height: Int,
+        educationView: View,
+    ) {
+        popupWindow =
+            additionalSystemViewContainerFactory.create(
+                windowManagerWrapper =
+                WindowManagerWrapper(context.getSystemService(WindowManager::class.java)),
+                taskId = taskId,
+                x = educationViewGlobalCoordinates.x,
+                y = educationViewGlobalCoordinates.y,
+                width = width,
+                height = height,
+                flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+                        WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+                view = educationView)
+    }
+
+    private fun View.setEducationColorScheme(educationColorScheme: EducationColorScheme) {
+        requireViewById<LinearLayout>(R.id.education_container).apply {
+            background.setTint(educationColorScheme.container)
+        }
+        requireViewById<TextView>(R.id.education_text).apply {
+            setTextColor(educationColorScheme.text)
+        }
+    }
+
+    private fun loadDimensionPixelSize(@DimenRes resourceId: Int): Int {
+        if (resourceId == Resources.ID_NULL) return 0
+        return context.resources.getDimensionPixelSize(resourceId)
+    }
+
+    /**
+     * The configuration for education view features:
+     *
+     * @property viewLayout Layout resource ID of the view to be used for education promo.
+     * @property viewGlobalCoordinates Global (screen) coordinates of the education.
+     * @property educationText Text to be added to the TextView of the promo.
+     * @property widthId res Id for education width
+     * @property heightId res Id for education height
+     */
+    data class EducationViewConfig(
+        @LayoutRes val viewLayout: Int,
+        val educationColorScheme: EducationColorScheme,
+        val viewGlobalCoordinates: Point,
+        val educationText: String,
+        @DimenRes val widthId: Int,
+        @DimenRes val heightId: Int
+    )
+
+    /**
+     * Color scheme of education view:
+     *
+     * @property container Color of the container of the education.
+     * @property text Text color of the [TextView] of education promo.
+     */
+    data class EducationColorScheme(
+        @ColorInt val container: Int,
+        @ColorInt val text: Int,
+    )
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
index c61b31e..4fa2744 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt
@@ -80,7 +80,7 @@
    * @param tooltipViewConfig features of tooltip.
    * @param taskId is used in the title of popup window created for the tooltip view.
    */
-  fun showEducationTooltip(tooltipViewConfig: EducationViewConfig, taskId: Int) {
+  fun showEducationTooltip(tooltipViewConfig: TooltipEducationViewConfig, taskId: Int) {
     hideEducationTooltip()
     tooltipView = createEducationTooltipView(tooltipViewConfig, taskId)
     animator = createAnimator()
@@ -93,7 +93,7 @@
 
   /** Create education view by inflating layout provided. */
   private fun createEducationTooltipView(
-      tooltipViewConfig: EducationViewConfig,
+      tooltipViewConfig: TooltipEducationViewConfig,
       taskId: Int,
   ): View {
     val tooltipView =
@@ -271,7 +271,7 @@
    * @property onEducationClickAction Lambda to be executed when the tooltip is clicked.
    * @property onDismissAction Lambda to be executed when the tooltip is dismissed.
    */
-  data class EducationViewConfig(
+  data class TooltipEducationViewConfig(
       @LayoutRes val tooltipViewLayout: Int,
       val tooltipColorScheme: TooltipColorScheme,
       val tooltipViewGlobalCoordinates: Point,
@@ -299,4 +299,4 @@
     UP,
     LEFT,
   }
-}
+}
\ No newline at end of file
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
index ff418c6..e43c3a6 100644
--- 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
@@ -21,10 +21,13 @@
 import android.content.Context
 import android.graphics.Rect
 import android.util.SparseArray
+import android.window.DisplayAreaInfo
+import android.window.WindowContainerTransaction
 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.DisplayChangeController
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopRepository
@@ -45,10 +48,16 @@
     private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
     private val returnToDragStartAnimator: ReturnToDragStartAnimator,
     private val taskRepository: DesktopRepository,
-) {
+) : DisplayChangeController.OnDisplayChangingListener {
     @VisibleForTesting
     var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
 
+    init {
+        // TODO(b/374309287): Move this interface implementation to
+        // [DesktopModeWindowDecorViewModel] when the migration is done.
+        displayController.addDisplayChangingController(this)
+    }
+
     fun snapToHalfScreen(
         taskInfo: ActivityManager.RunningTaskInfo,
         desktopModeWindowDecoration: DesktopModeWindowDecoration,
@@ -102,7 +111,20 @@
 
     fun onUserChange() {
         for (tilingHandler in tilingTransitionHandlerByDisplayId.valueIterator()) {
-            tilingHandler.onUserChange()
+            tilingHandler.resetTilingSession()
         }
     }
+
+    override fun onDisplayChange(
+        displayId: Int,
+        fromRotation: Int,
+        toRotation: Int,
+        newDisplayAreaInfo: DisplayAreaInfo?,
+        t: WindowContainerTransaction?,
+    ) {
+        // Exit if the rotation hasn't changed or is changed by 180 degrees. [fromRotation] and
+        // [toRotation] can be one of the [@Surface.Rotation] values.
+        if ((fromRotation % 2 == toRotation % 2)) return
+        tilingTransitionHandlerByDisplayId.get(displayId)?.resetTilingSession()
+    }
 }
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
index 9bf1304..209eb5e 100644
--- 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
@@ -23,6 +23,7 @@
 import android.graphics.Region
 import android.os.Binder
 import android.view.LayoutInflater
+import android.view.RoundedCorner
 import android.view.SurfaceControl
 import android.view.SurfaceControlViewHost
 import android.view.View
@@ -53,12 +54,14 @@
     private val transitionHandler: DesktopTilingWindowDecoration,
     private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
     private var dividerBounds: Rect,
+    private val displayContext: Context,
 ) : 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
+    private val maxRoundedCornerRadius = getMaxRoundedCornerRadius()
 
     /**
      * Gets bounds of divider window with screen based coordinate on the param Rect.
@@ -93,7 +96,11 @@
         getDividerBounds(tmpDividerBounds)
         dividerView.setup(this, tmpDividerBounds)
         t.setRelativeLayer(leash, relativeLeash, 1)
-            .setPosition(leash, dividerBounds.left.toFloat(), dividerBounds.top.toFloat())
+            .setPosition(
+                leash,
+                dividerBounds.left.toFloat() - maxRoundedCornerRadius,
+                dividerBounds.top.toFloat(),
+            )
             .show(leash)
         syncQueue.runInSync { transaction ->
             transaction.merge(t)
@@ -144,7 +151,7 @@
      */
     override fun onDividerMove(pos: Int): Boolean {
         val t = transactionSupplier.get()
-        t.setPosition(leash, pos.toFloat(), dividerBounds.top.toFloat())
+        t.setPosition(leash, pos.toFloat() - maxRoundedCornerRadius, dividerBounds.top.toFloat())
         val dividerWidth = dividerBounds.width()
         dividerBounds.set(pos, dividerBounds.top, pos + dividerWidth, dividerBounds.bottom)
         return transitionHandler.onDividerHandleMoved(dividerBounds, t)
@@ -157,7 +164,7 @@
     override fun onDividerMovedEnd(pos: Int) {
         setSlippery(true)
         val t = transactionSupplier.get()
-        t.setPosition(leash, pos.toFloat(), dividerBounds.top.toFloat())
+        t.setPosition(leash, pos.toFloat() - maxRoundedCornerRadius, dividerBounds.top.toFloat())
         val dividerWidth = dividerBounds.width()
         dividerBounds.set(pos, dividerBounds.top, pos + dividerWidth, dividerBounds.bottom)
         transitionHandler.onDividerHandleDragEnd(dividerBounds, t)
@@ -166,7 +173,7 @@
     private fun getWindowManagerParams(): WindowManager.LayoutParams {
         val lp =
             WindowManager.LayoutParams(
-                dividerBounds.width(),
+                dividerBounds.width() + 2 * maxRoundedCornerRadius,
                 dividerBounds.height(),
                 TYPE_DOCK_DIVIDER,
                 FLAG_NOT_FOCUSABLE or
@@ -225,4 +232,15 @@
         }
         viewHost.relayout(lp)
     }
+
+    private fun getMaxRoundedCornerRadius(): Int {
+        val display = displayContext.display
+        return listOf(
+                RoundedCorner.POSITION_TOP_LEFT,
+                RoundedCorner.POSITION_TOP_RIGHT,
+                RoundedCorner.POSITION_BOTTOM_RIGHT,
+                RoundedCorner.POSITION_BOTTOM_LEFT,
+            )
+            .maxOf { position -> display.getRoundedCorner(position)?.getRadius() ?: 0 }
+    }
 }
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
index 6ea1d14..c46767c 100644
--- 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
@@ -23,10 +23,12 @@
 import android.graphics.Bitmap
 import android.graphics.Rect
 import android.os.IBinder
+import android.os.UserHandle
 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
@@ -125,7 +127,6 @@
                     resizeMetadata.getLeash(),
                     startBounds = currentBounds,
                     endBounds = destinationBounds,
-                    isResizable = taskInfo.isResizeable,
                 )
             }
         }
@@ -194,6 +195,7 @@
         val builder = SurfaceControl.Builder()
         rootTdaOrganizer.attachToDisplayArea(displayId, builder)
         val leash = builder.setName(TILING_DIVIDER_TAG).setContainerLayer().build()
+        val displayContext = displayController.getDisplayContext(displayId) ?: return null
         val tilingManager =
             displayLayout?.let {
                 dividerBounds = inflateDividerBounds(it)
@@ -206,6 +208,7 @@
                     this,
                     transactionSupplier,
                     dividerBounds,
+                    displayContext,
                 )
             }
         // a leash to present the divider on top of, without re-parenting.
@@ -342,7 +345,9 @@
 
     private fun isMinimized(changeMode: Int, infoType: Int): Boolean {
         return (changeMode == TRANSIT_TO_BACK &&
-            (infoType == TRANSIT_MINIMIZE || infoType == TRANSIT_TO_BACK))
+            (infoType == TRANSIT_MINIMIZE ||
+                infoType == TRANSIT_TO_BACK ||
+                infoType == TRANSIT_OPEN))
     }
 
     class AppResizingHelper(
@@ -358,6 +363,8 @@
         private lateinit var resizeVeilBitmap: Bitmap
         private lateinit var resizeVeil: ResizeVeil
         private val displayContext = displayController.getDisplayContext(taskInfo.displayId)
+        private val userContext =
+            context.createContextAsUser(UserHandle.of(taskInfo.userId), /* flags= */ 0)
 
         fun initIfNeeded() {
             if (!isInitialised) {
@@ -376,7 +383,7 @@
                 displayContext?.let {
                     createIconFactory(displayContext, R.dimen.desktop_mode_resize_veil_icon_size)
                 } ?: return
-            val pm = context.getApplicationContext().getPackageManager()
+            val pm = userContext.getPackageManager()
             val activityInfo = pm.getActivityInfo(baseActivity, 0 /* flags */)
             val provider = IconProvider(displayContext)
             val appIconDrawable = provider.getIcon(activityInfo)
@@ -478,7 +485,7 @@
         }
     }
 
-    fun onUserChange() {
+    fun resetTilingSession() {
         if (leftTaskResizingHelper != null) {
             removeTask(leftTaskResizingHelper, taskVanished = false, shouldDelayUpdate = true)
             leftTaskResizingHelper = null
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
index 065a5d7..8922905 100644
--- 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
@@ -49,11 +49,10 @@
     var handleRegionWidth: Int = 0
     private var handleRegionHeight = 0
     private var lastAcceptedPos = 0
-    @VisibleForTesting
-    var handleStartY = 0
-    @VisibleForTesting
-    var handleEndY = 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.
@@ -119,7 +118,7 @@
             val dividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width)
             val backgroundLeft = (width - dividerSize) / 2
             val backgroundTop = 0
-            val backgroundRight = left + dividerSize
+            val backgroundRight = backgroundLeft + dividerSize
             val backgroundBottom = height
             backgroundRect.set(backgroundLeft, backgroundTop, backgroundRight, backgroundBottom)
         }
@@ -209,7 +208,6 @@
                 if (!isWithinHandleRegion(yTouchPosInDivider)) return true
                 callback.onDividerMoveStart(touchPos)
                 setTouching()
-                startPos = touchPos
                 canResize = true
             }
 
@@ -223,19 +221,20 @@
                 val pos = dividerBounds.left + touchPos - startPos
                 if (callback.onDividerMove(pos)) {
                     lastAcceptedPos = touchPos
+                    resized = true
                 }
             }
 
             MotionEvent.ACTION_CANCEL,
             MotionEvent.ACTION_UP -> {
                 if (!canResize) return true
-                dividerBounds.left = dividerBounds.left + lastAcceptedPos - startPos
-                if (moving) {
+                if (moving && resized) {
+                    dividerBounds.left = dividerBounds.left + lastAcceptedPos - startPos
                     callback.onDividerMovedEnd(dividerBounds.left)
-                    moving = false
-                    canResize = false
                 }
-
+                moving = false
+                canResize = false
+                resized = false
                 releaseTouching()
             }
         }
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 b43a983..b5700ff 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,10 +36,13 @@
 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).
@@ -66,12 +69,10 @@
     ) : 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
@@ -111,54 +112,21 @@
     ) {
         captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
         this.taskInfo = taskInfo
-        this.position.set(position)
-        this.width = width
-        this.height = height
-        if (!isCaptionVisible && statusBarInputLayer != null) {
-            detachStatusBarInputLayer()
+        // 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()
             return
         }
-    }
-
-    fun bindStatusBarInputLayer(
-        statusBarLayer: AdditionalSystemViewContainer
-    ) {
-        // Input layer view modification takes a significant amount of time;
+        // Input layer view creation / modification takes a significant amount of time;
         // post them so we don't hold up DesktopModeWindowDecoration#relayout.
-        if (statusBarLayer == statusBarInputLayer) {
+        if (statusBarInputLayerExists) {
             handler.post { updateStatusBarInputLayer(position) }
-            return
-        }
-        // Remove the old input layer when changing to a new one.
-        if (statusBarInputLayer != null) detachStatusBarInputLayer()
-        if (statusBarLayer.view.visibility == View.INVISIBLE) {
-            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
+        } else {
+            // Input layer is created on a delay; prevent multiple from being created.
+            statusBarInputLayerExists = true
+            handler.post { createStatusBarInputLayer(position, width, height) }
         }
     }
 
@@ -170,6 +138,40 @@
         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(
@@ -222,12 +224,15 @@
     }
 
     /**
-     * Remove the input listeners from the input layer and remove it from this view holder.
+     * Remove the input layer from [WindowManager]. Should be used when caption handle
+     * is not visible.
      */
-    fun detachStatusBarInputLayer() {
-        statusBarInputLayer?.view?.setOnTouchListener(null)
-        statusBarInputLayer?.view?.setOnHoverListener(null)
-        statusBarInputLayer = null
+    fun disposeStatusBarInputLayer() {
+        statusBarInputLayerExists = false
+        handler.post {
+            statusBarInputLayer?.releaseView()
+            statusBarInputLayer = null
+        }
     }
 
     private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
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 72d4dc6..13a8518 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
@@ -700,7 +700,7 @@
                 eq(tInfo), eq(st), eq(ft), eq(callback));
 
         mBackTransitionHandler.onAnimationFinished();
-        final TransitionInfo.Change openToClose = createAppChange(openTaskId, TRANSIT_CLOSE,
+        final TransitionInfo.Change openToClose = createAppChangeFromChange(open, TRANSIT_CLOSE,
                 FLAG_BACK_GESTURE_ANIMATED);
         tInfo2 = createTransitionInfo(TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION, openToClose);
         mBackTransitionHandler.mClosePrepareTransition = mock(IBinder.class);
@@ -830,6 +830,16 @@
         return change;
     }
 
+    private TransitionInfo.Change createAppChangeFromChange(
+            TransitionInfo.Change originalChange, @TransitionInfo.TransitionMode int mode,
+            @TransitionInfo.ChangeFlags int flags) {
+        final TransitionInfo.Change change = new TransitionInfo.Change(
+                originalChange.getTaskInfo().token, originalChange.getLeash());
+        change.setMode(mode);
+        change.setFlags(flags);
+        return change;
+    }
+
     private static TransitionInfo createTransitionInfo(
             @WindowManager.TransitionType int type, TransitionInfo.Change ... changes) {
         final TransitionInfo info = new TransitionInfo(type, 0);
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..df061e3 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,16 +31,22 @@
 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
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.window.flags.Flags
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler.PendingMixedTransition
 import com.android.wm.shell.freeform.FreeformTaskTransitionHandler
+import com.android.wm.shell.sysui.ShellInit
 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
@@ -48,9 +54,13 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
+import org.mockito.Mockito.times
 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,9 +81,12 @@
     @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
+    @Mock lateinit var shellInit: ShellInit
+    @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
 
     private lateinit var mixedHandler: DesktopMixedTransitionHandler
 
@@ -86,8 +99,11 @@
                 desktopRepository,
                 freeformTaskTransitionHandler,
                 closeDesktopTaskTransitionHandler,
+                desktopImmersiveController,
                 interactionJankMonitor,
-                mockHandler
+                mockHandler,
+                shellInit,
+                rootTaskDisplayAreaOrganizer,
             )
     }
 
@@ -146,7 +162,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 +180,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 +190,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 +208,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 +246,324 @@
             )
     }
 
+    @Test
+    @DisableFlags(
+        Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP,
+        Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+    fun startLaunchTransition_immersiveAndAppLaunchFlagsDisabled_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_DESKTOP_APP_LAUNCH_TRANSITIONS)
+    fun startLaunchTransition_desktopAppLaunchEnabled_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_DESKTOP_APP_LAUNCH_TRANSITIONS)
+    fun startAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
+        val wct = WindowContainerTransaction()
+        val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val launchTaskChange = createChange(launchingTask)
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(transition)
+
+        mixedHandler.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            taskId = launchingTask.taskId,
+            minimizingTaskId = null,
+        )
+        mixedHandler.startAnimation(
+            transition,
+            createTransitionInfo(
+                TRANSIT_OPEN,
+                listOf(launchTaskChange)
+            ),
+            SurfaceControl.Transaction(),
+            SurfaceControl.Transaction(),
+        ) { }
+
+        verify(rootTaskDisplayAreaOrganizer, times(0))
+            .reparentToDisplayArea(anyInt(), any(), any())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+    fun startAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
+        val wct = WindowContainerTransaction()
+        val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val launchTaskChange = createChange(launchingTask)
+        val minimizeChange = createChange(minimizingTask)
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(transition)
+
+        mixedHandler.startLaunchTransition(
+            transitionType = TRANSIT_OPEN,
+            wct = wct,
+            taskId = launchingTask.taskId,
+            minimizingTaskId = minimizingTask.taskId,
+        )
+        mixedHandler.startAnimation(
+            transition,
+            createTransitionInfo(
+                TRANSIT_OPEN,
+                listOf(launchTaskChange, minimizeChange)
+            ),
+            SurfaceControl.Transaction(),
+            SurfaceControl.Transaction(),
+        ) { }
+
+        verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(
+            anyInt(), eq(minimizeChange.leash), any())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+    fun addPendingAndAnimateLaunchTransition_noMinimizeChange_doesNotReparentMinimizeChange() {
+        val wct = WindowContainerTransaction()
+        val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val launchTaskChange = createChange(launchingTask)
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(transition)
+
+        mixedHandler.addPendingMixedTransition(
+            PendingMixedTransition.Launch(
+                transition = transition,
+                launchingTask = launchingTask.taskId,
+                minimizingTask = null,
+                exitingImmersiveTask = null,
+            )
+        )
+        mixedHandler.startAnimation(
+            transition,
+            createTransitionInfo(
+                TRANSIT_OPEN,
+                listOf(launchTaskChange)
+            ),
+            SurfaceControl.Transaction(),
+            SurfaceControl.Transaction(),
+        ) { }
+
+        verify(rootTaskDisplayAreaOrganizer, times(0))
+            .reparentToDisplayArea(anyInt(), any(), any())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS)
+    fun addPendingAndAnimateLaunchTransition_withMinimizeChange_reparentsMinimizeChange() {
+        val wct = WindowContainerTransaction()
+        val launchingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val minimizingTask = createTask(WINDOWING_MODE_FREEFORM)
+        val launchTaskChange = createChange(launchingTask)
+        val minimizeChange = createChange(minimizingTask)
+        val transition = Binder()
+        whenever(transitions.startTransition(eq(TRANSIT_OPEN), eq(wct), anyOrNull()))
+            .thenReturn(transition)
+
+        mixedHandler.addPendingMixedTransition(
+            PendingMixedTransition.Launch(
+                transition = transition,
+                launchingTask = launchingTask.taskId,
+                minimizingTask = minimizingTask.taskId,
+                exitingImmersiveTask = null,
+            )
+        )
+        mixedHandler.startAnimation(
+            transition,
+            createTransitionInfo(
+                TRANSIT_OPEN,
+                listOf(launchTaskChange, minimizeChange)
+            ),
+            SurfaceControl.Transaction(),
+            SurfaceControl.Transaction(),
+        ) { }
+
+        verify(rootTaskDisplayAreaOrganizer).reparentToDisplayArea(
+            anyInt(), eq(minimizeChange.leash), any())
+    }
+
+    @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 +579,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..414c1a6 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
@@ -50,6 +53,7 @@
 import org.mockito.Mockito.spy
 import org.mockito.kotlin.any
 import org.mockito.kotlin.never
+import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
@@ -58,12 +62,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 +78,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 +98,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 +165,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 +189,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 +215,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()
@@ -214,8 +228,35 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+    fun updateTaskVisibility_multipleTasks_persistsVisibleTasks() =
+        runTest(StandardTestDispatcher()) {
+            repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
+            repo.updateTask(DEFAULT_DISPLAY, taskId = 2, isVisible = true)
+
+            inOrder(persistentRepository).run {
+                verify(persistentRepository)
+                    .addOrUpdateDesktop(
+                        DEFAULT_USER_ID,
+                        DEFAULT_DESKTOP_ID,
+                        visibleTasks = ArraySet(arrayOf(1)),
+                        minimizedTasks = ArraySet(),
+                        freeformTasksInZOrder = arrayListOf()
+                    )
+                verify(persistentRepository)
+                    .addOrUpdateDesktop(
+                        DEFAULT_USER_ID,
+                        DEFAULT_DESKTOP_ID,
+                        visibleTasks = ArraySet(arrayOf(1, 2)),
+                        minimizedTasks = ArraySet(),
+                        freeformTasksInZOrder = arrayListOf()
+                    )
+            }
+        }
+
+    @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 +270,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 +285,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 +306,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 +326,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 +339,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 +351,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 +365,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 +378,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 +389,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 +414,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 +441,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 +464,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 +497,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 +543,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 +564,7 @@
                     .addOrUpdateDesktop(
                         DEFAULT_USER_ID,
                         DEFAULT_DESKTOP_ID,
-                        visibleTasks = ArraySet(),
+                        visibleTasks = ArraySet(arrayOf(5)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(6, 5)
                     )
@@ -528,7 +572,7 @@
                     .addOrUpdateDesktop(
                         DEFAULT_USER_ID,
                         DEFAULT_DESKTOP_ID,
-                        visibleTasks = ArraySet(),
+                        visibleTasks = ArraySet(arrayOf(5, 6)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(7, 6, 5)
                     )
@@ -536,12 +580,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 +593,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 +608,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 +630,7 @@
                     .addOrUpdateDesktop(
                         DEFAULT_USER_ID,
                         DEFAULT_DESKTOP_ID,
-                        visibleTasks = ArraySet(),
+                        visibleTasks = ArraySet(arrayOf(5)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(6, 5)
                     )
@@ -594,15 +638,15 @@
                     .addOrUpdateDesktop(
                         DEFAULT_USER_ID,
                         DEFAULT_DESKTOP_ID,
-                        visibleTasks = ArraySet(),
+                        visibleTasks = ArraySet(arrayOf(5, 6)),
                         minimizedTasks = ArraySet(),
                         freeformTasksInZOrder = arrayListOf(7, 6, 5)
                     )
-                verify(persistentRepository)
+                verify(persistentRepository, times(2))
                     .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 +654,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 +666,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 +680,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 +705,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 +717,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 +742,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 +754,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 +780,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 +791,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 +804,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 +815,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 +826,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 +894,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 +926,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 +937,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 +948,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 +1006,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 0a6dfbf..b157d55 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
@@ -196,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
@@ -223,6 +225,7 @@
   @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
@@ -263,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,
@@ -288,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)
@@ -323,6 +333,7 @@
         transitions,
         keyguardManager,
         mReturnToDragStartAnimator,
+        desktopMixedTransitionHandler,
         enterDesktopTransitionHandler,
         exitDesktopTransitionHandler,
         dragAndDropTransitionHandler,
@@ -1389,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)
   }
@@ -1398,10 +1409,17 @@
   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(),
+      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)
@@ -1443,7 +1461,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)
   }
@@ -1453,10 +1471,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(), 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)
@@ -1695,7 +1716,7 @@
   }
 
   @Test
-  fun onDesktopWindowMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
+  fun onTaskMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
     val task = setUpFreeformTask()
     val transition = Binder()
     whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
@@ -1791,7 +1812,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)
 
@@ -2382,8 +2406,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")
@@ -2497,8 +2520,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")
@@ -2573,8 +2595,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)
 
@@ -2897,6 +2918,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),
+      anyOrNull(),
+    )
+  }
+
+  @Test
   fun enterSplit_freeformTaskIsMovedToSplit() {
     val task1 = setUpFreeformTask()
     val task2 = setUpFreeformTask()
@@ -2928,8 +3066,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)
 
@@ -3105,7 +3242,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)
 
@@ -3205,7 +3345,7 @@
       eq(mockSurface),
       eq(currentDragBounds),
       eq(bounds),
-      eq(true)
+      anyOrNull(),
     )
     verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
       ResizeTrigger.SNAP_LEFT_MENU,
@@ -3262,7 +3402,7 @@
       eq(mockSurface),
       eq(currentDragBounds),
       eq(preDragBounds),
-      eq(false)
+      any(),
     )
     verify(desktopModeEventLogger, never()).logTaskResizingStarted(
       any(),
@@ -3422,7 +3562,6 @@
     )
   }
 
-
   @Test
   fun onUnhandledDrag_newFreeformIntent() {
     testOnUnhandledDrag(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
@@ -3507,7 +3646,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,7 +3667,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)
@@ -3541,8 +3688,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(), anyInt(), anyOrNull(), anyOrNull()))
+      .thenReturn(transition)
 
     controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
@@ -3558,8 +3710,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(), anyOrNull()))
+      .thenReturn(transition)
 
     controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
@@ -3808,11 +3965,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)
     }
@@ -3915,13 +4068,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(
@@ -3929,7 +4080,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 {
@@ -3948,6 +4098,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(), anyOrNull())
+    return arg.value
+  }
+
   private fun getLatestEnterDesktopWct(): WindowContainerTransaction {
     val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
     verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
@@ -4046,7 +4206,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 4d7e47f..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()
@@ -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/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
index fefa933..a82e5e8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
@@ -19,8 +19,6 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
-import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
-
 import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN;
 
 import static org.junit.Assert.assertTrue;
@@ -28,6 +26,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.animation.AnimatorTestRule;
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.WindowConfiguration;
@@ -36,6 +35,8 @@
 import android.graphics.Point;
 import android.os.Handler;
 import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.util.DisplayMetrics;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -53,17 +54,23 @@
 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;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
 import java.util.function.Supplier;
 
 /** Tests of {@link com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler} */
 @SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
 public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase {
 
+    @Rule
+    public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
+
     @Mock
     private Transitions mTransitions;
     @Mock
@@ -105,7 +112,7 @@
     }
 
     @Test
-    public void testTransitExitDesktopModeAnimation() throws Throwable {
+    public void testTransitExitDesktopModeAnimation() {
         final int transitionType = TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN;
         final int taskId = 1;
         WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -118,21 +125,16 @@
         TransitionInfo.Change change =
                 createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FULLSCREEN);
         TransitionInfo info = createTransitionInfo(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN, change);
-        ArrayList<Exception> exceptions = new ArrayList<>();
-        runOnUiThread(() -> {
-            try {
-                assertTrue(mExitDesktopTaskTransitionHandler
-                        .startAnimation(mToken, info,
-                                new SurfaceControl.Transaction(),
-                                new SurfaceControl.Transaction(),
-                                mTransitionFinishCallback));
-            } catch (Exception e) {
-                exceptions.add(e);
-            }
-        });
-        if (!exceptions.isEmpty()) {
-            throw exceptions.get(0);
-        }
+
+        final boolean animated = mExitDesktopTaskTransitionHandler
+                .startAnimation(mToken, info,
+                        new SurfaceControl.Transaction(),
+                        new SurfaceControl.Transaction(),
+                        mTransitionFinishCallback);
+        mAnimatorTestRule.advanceTimeBy(
+                ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION);
+
+        assertTrue(animated);
     }
 
     private TransitionInfo.Change createChange(@WindowManager.TransitionType int type, int taskId,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepositoryTest.kt
index e3caf2e..38c6ed9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepositoryTest.kt
@@ -49,7 +49,10 @@
     val taskInfo = createTaskInfo(WINDOWING_MODE_FULLSCREEN, GMAIL_PACKAGE_NAME)
     val appHandleCaptionState =
         CaptionState.AppHandle(
-            taskInfo, false, Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3))
+          runningTaskInfo = taskInfo,
+          isHandleMenuExpanded = false,
+          globalAppHandleBounds = Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
+          isCapturedLinkAvailable = false)
 
     captionHandleRepository.notifyCaptionChanged(appHandleCaptionState)
 
@@ -61,7 +64,10 @@
     val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, GMAIL_PACKAGE_NAME)
     val appHeaderCaptionState =
         CaptionState.AppHeader(
-            taskInfo, true, Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3))
+          runningTaskInfo = taskInfo,
+          isHeaderMenuExpanded = true,
+          globalAppChipBounds = Rect(/* left= */ 0, /* top= */ 1, /* right= */ 2, /* bottom= */ 3),
+          isCapturedLinkAvailable = false)
 
     captionHandleRepository.notifyCaptionChanged(appHeaderCaptionState)
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
index 7dbadc9..d94186c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
@@ -84,7 +84,7 @@
   private val testDataStoreFlow = MutableStateFlow(createWindowingEducationProto())
   private val testCaptionStateFlow = MutableStateFlow<CaptionState>(CaptionState.NoCaption)
   private val educationConfigCaptor =
-      argumentCaptor<DesktopWindowingEducationTooltipController.EducationViewConfig>()
+      argumentCaptor<DesktopWindowingEducationTooltipController.TooltipEducationViewConfig>()
   @Mock private lateinit var mockEducationFilter: AppHandleEducationFilter
   @Mock private lateinit var mockDataStoreRepository: AppHandleEducationDatastoreRepository
   @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository
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 8dd1545..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,6 +32,7 @@
 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;
@@ -59,9 +62,8 @@
 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)
@@ -84,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);
@@ -139,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);
@@ -154,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);
@@ -169,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);
@@ -188,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);
 
@@ -199,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/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index e74c804..bcb7461 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -280,7 +280,7 @@
 
     private void preparePipSurfaceTransactionHelper() {
         doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
-                .crop(any(), any(), any());
+                .cropAndPosition(any(), any(), any());
         doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
                 .resetScale(any(), any(), any());
         doReturn(mMockPipSurfaceTransactionHelper).when(mMockPipSurfaceTransactionHelper)
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/pip2/animation/PipExpandAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
new file mode 100644
index 0000000..e19a10a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipExpandAnimatorTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.graphics.Matrix;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.Surface;
+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 PipExpandAnimator}.
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipExpandAnimatorTest {
+
+    @Mock private Context mMockContext;
+
+    @Mock private Resources mMockResources;
+
+    @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
+
+    @Mock private SurfaceControl.Transaction mMockTransaction;
+
+    @Mock private SurfaceControl.Transaction mMockStartTransaction;
+
+    @Mock private SurfaceControl.Transaction mMockFinishTransaction;
+
+    @Mock private Runnable mMockStartCallback;
+
+    @Mock private Runnable mMockEndCallback;
+
+    private PipExpandAnimator mPipExpandAnimator;
+    private Rect mBaseBounds;
+    private Rect mStartBounds;
+    private Rect mEndBounds;
+    private Rect mSourceRectHint;
+    @Surface.Rotation private int mRotation;
+    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);
+        // No-op on the mMockTransaction
+        when(mMockTransaction.setAlpha(any(SurfaceControl.class), anyFloat()))
+                .thenReturn(mMockTransaction);
+        when(mMockTransaction.setCrop(any(SurfaceControl.class), any(Rect.class)))
+                .thenReturn(mMockTransaction);
+        when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+                .thenReturn(mMockTransaction);
+        when(mMockTransaction.setCornerRadius(any(SurfaceControl.class), anyFloat()))
+                .thenReturn(mMockTransaction);
+        when(mMockTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat()))
+                .thenReturn(mMockTransaction);
+        // Do the same for mMockFinishTransaction
+        when(mMockFinishTransaction.setAlpha(any(SurfaceControl.class), anyFloat()))
+                .thenReturn(mMockFinishTransaction);
+        when(mMockFinishTransaction.setCrop(any(SurfaceControl.class), any(Rect.class)))
+                .thenReturn(mMockFinishTransaction);
+        when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+                .thenReturn(mMockFinishTransaction);
+        when(mMockFinishTransaction.setCornerRadius(any(SurfaceControl.class), anyFloat()))
+                .thenReturn(mMockFinishTransaction);
+        when(mMockFinishTransaction.setShadowRadius(any(SurfaceControl.class), anyFloat()))
+                .thenReturn(mMockFinishTransaction);
+
+        mTestLeash = new SurfaceControl.Builder()
+                .setContainerLayer()
+                .setName("PipExpandAnimatorTest")
+                .setCallsite("PipExpandAnimatorTest")
+                .build();
+    }
+
+    @Test
+    public void setAnimationStartCallback_expand_callbackStartCallback() {
+        mRotation = Surface.ROTATION_0;
+        mBaseBounds = new Rect(0, 0, 1_000, 2_000);
+        mStartBounds = new Rect(500, 1_000, 1_000, 2_000);
+        mEndBounds = new Rect(mBaseBounds);
+        mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash,
+                mMockStartTransaction, mMockFinishTransaction,
+                mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint,
+                mRotation);
+        mPipExpandAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+        mPipExpandAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipExpandAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipExpandAnimator.start();
+            mPipExpandAnimator.pause();
+        });
+
+        verify(mMockStartCallback).run();
+        verifyZeroInteractions(mMockEndCallback);
+    }
+
+    @Test
+    public void setAnimationEndCallback_expand_callbackStartAndEndCallback() {
+        mRotation = Surface.ROTATION_0;
+        mBaseBounds = new Rect(0, 0, 1_000, 2_000);
+        mStartBounds = new Rect(500, 1_000, 1_000, 2_000);
+        mEndBounds = new Rect(mBaseBounds);
+        mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash,
+                mMockStartTransaction, mMockFinishTransaction,
+                mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint,
+                mRotation);
+        mPipExpandAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+        mPipExpandAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipExpandAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipExpandAnimator.start();
+            mPipExpandAnimator.end();
+        });
+
+        verify(mMockStartCallback).run();
+        verify(mMockEndCallback).run();
+    }
+
+    @Test
+    public void onAnimationEnd_expand_leashIsFullscreen() {
+        mRotation = Surface.ROTATION_0;
+        mBaseBounds = new Rect(0, 0, 1_000, 2_000);
+        mStartBounds = new Rect(500, 1_000, 1_000, 2_000);
+        mEndBounds = new Rect(mBaseBounds);
+        mPipExpandAnimator = new PipExpandAnimator(mMockContext, mTestLeash,
+                mMockStartTransaction, mMockFinishTransaction,
+                mBaseBounds, mStartBounds, mEndBounds, mSourceRectHint,
+                mRotation);
+        mPipExpandAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+        mPipExpandAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipExpandAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipExpandAnimator.start();
+            clearInvocations(mMockTransaction);
+            mPipExpandAnimator.end();
+        });
+
+        verify(mMockTransaction).setCrop(mTestLeash, mEndBounds);
+        verify(mMockTransaction).setCornerRadius(mTestLeash, 0f);
+        verify(mMockTransaction).setShadowRadius(mTestLeash, 0f);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
new file mode 100644
index 0000000..0adb50b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/animation/PipResizeAnimatorTest.java
@@ -0,0 +1,276 @@
+/*
+ * 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.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.mockito.kotlin.MatchersKt.eq;
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+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.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test against {@link PipResizeAnimator}.
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class PipResizeAnimatorTest {
+
+    private static final float FLOAT_COMPARISON_DELTA = 0.001f;
+
+    @Mock private Context mMockContext;
+
+    @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
+
+    @Mock private SurfaceControl.Transaction mMockTransaction;
+
+    @Mock private SurfaceControl.Transaction mMockStartTransaction;
+
+    @Mock private SurfaceControl.Transaction mMockFinishTransaction;
+
+    @Mock private Runnable mMockStartCallback;
+
+    @Mock private Runnable mMockEndCallback;
+
+    private PipResizeAnimator mPipResizeAnimator;
+    private Rect mBaseBounds;
+    private Rect mStartBounds;
+    private Rect mEndBounds;
+    private SurfaceControl mTestLeash;
+    private ArgumentCaptor<Matrix> mArgumentCaptor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mMockFactory.getTransaction()).thenReturn(mMockTransaction);
+        when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+                .thenReturn(mMockTransaction);
+        when(mMockStartTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+                .thenReturn(mMockStartTransaction);
+        when(mMockFinishTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
+                .thenReturn(mMockFinishTransaction);
+
+        mArgumentCaptor = ArgumentCaptor.forClass(Matrix.class);
+        mTestLeash = new SurfaceControl.Builder()
+                .setContainerLayer()
+                .setName("PipResizeAnimatorTest")
+                .setCallsite("PipResizeAnimatorTest")
+                .build();
+    }
+
+    @Test
+    public void setAnimationStartCallback_resize_callbackStartCallback() {
+        mBaseBounds = new Rect(100, 100, 500, 500);
+        mStartBounds = new Rect(200, 200, 1_000, 1_000);
+        mEndBounds = new Rect(mBaseBounds);
+        final int duration = 10;
+        final float delta = 0;
+        mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+                mMockStartTransaction, mMockFinishTransaction,
+                mBaseBounds, mStartBounds, mEndBounds,
+                duration, delta);
+
+        mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipResizeAnimator.start();
+            mPipResizeAnimator.pause();
+        });
+
+        verify(mMockStartCallback).run();
+        verifyZeroInteractions(mMockEndCallback);
+    }
+
+    @Test
+    public void setAnimationEndCallback_resize_callbackStartAndEndCallback() {
+        mBaseBounds = new Rect(100, 100, 500, 500);
+        mStartBounds = new Rect(200, 200, 1_000, 1_000);
+        mEndBounds = new Rect(mBaseBounds);
+        final int duration = 10;
+        final float delta = 0;
+        mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+                mMockStartTransaction, mMockFinishTransaction,
+                mBaseBounds, mStartBounds, mEndBounds,
+                duration, delta);
+
+        mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipResizeAnimator.start();
+            mPipResizeAnimator.end();
+        });
+
+        verify(mMockStartCallback).run();
+        verify(mMockEndCallback).run();
+    }
+
+    @Test
+    public void onAnimationEnd_resizeDown_sizeChanged() {
+        // Resize from 800x800 to 400x400, eg. resize down
+        mBaseBounds = new Rect(100, 100, 500, 500);
+        mStartBounds = new Rect(200, 200, 1_000, 1_000);
+        mEndBounds = new Rect(mBaseBounds);
+        final int duration = 10;
+        final float delta = 0;
+        final float[] matrix = new float[9];
+        mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+                mMockStartTransaction, mMockFinishTransaction,
+                mBaseBounds, mStartBounds, mEndBounds,
+                duration, delta);
+        mPipResizeAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+        mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipResizeAnimator.start();
+            clearInvocations(mMockTransaction);
+            mPipResizeAnimator.end();
+        });
+
+        // Start transaction scales down from its final state
+        verify(mMockStartTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+        mArgumentCaptor.getValue().getValues(matrix);
+        assertEquals(matrix[Matrix.MSCALE_X],
+                mStartBounds.width() / (float) mEndBounds.width(), FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MSCALE_Y],
+                mStartBounds.height() / (float) mEndBounds.height(), FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_X], mStartBounds.left, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_Y], mStartBounds.top, FLOAT_COMPARISON_DELTA);
+
+        // Final animation transaction scales to 1 and puts the leash at final position
+        verify(mMockTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+        mArgumentCaptor.getValue().getValues(matrix);
+        assertEquals(matrix[Matrix.MSCALE_X], 1f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+
+        // Finish transaction resets scale and puts the leash at final position
+        verify(mMockFinishTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+        mArgumentCaptor.getValue().getValues(matrix);
+        assertEquals(matrix[Matrix.MSCALE_X], 1f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+    }
+
+    @Test
+    public void onAnimationEnd_resizeUp_sizeChanged() {
+        // Resize from 400x400 to 800x800, eg. resize up
+        mBaseBounds = new Rect(200, 200, 1_000, 1_000);
+        mStartBounds = new Rect(100, 100, 500, 500);
+        mEndBounds = new Rect(mBaseBounds);
+        final int duration = 10;
+        final float delta = 0;
+        final float[] matrix = new float[9];
+        mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+                mMockStartTransaction, mMockFinishTransaction,
+                mBaseBounds, mStartBounds, mEndBounds,
+                duration, delta);
+        mPipResizeAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+        mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipResizeAnimator.start();
+            clearInvocations(mMockTransaction);
+            mPipResizeAnimator.end();
+        });
+
+        // Start transaction scales up from its final state
+        verify(mMockStartTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+        mArgumentCaptor.getValue().getValues(matrix);
+        assertEquals(matrix[Matrix.MSCALE_X],
+                mStartBounds.width() / (float) mEndBounds.width(), FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MSCALE_Y],
+                mStartBounds.height() / (float) mEndBounds.height(), FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_X], mStartBounds.left, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_Y], mStartBounds.top, FLOAT_COMPARISON_DELTA);
+
+        // Final animation transaction scales to 1 and puts the leash at final position
+        verify(mMockTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+        mArgumentCaptor.getValue().getValues(matrix);
+        assertEquals(matrix[Matrix.MSCALE_X], 1f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+
+        // Finish transaction resets scale and puts the leash at final position
+        verify(mMockFinishTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+        mArgumentCaptor.getValue().getValues(matrix);
+        assertEquals(matrix[Matrix.MSCALE_X], 1f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MSCALE_Y], 1f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_X], mEndBounds.left, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MTRANS_Y], mEndBounds.top, FLOAT_COMPARISON_DELTA);
+    }
+
+    @Test
+    public void onAnimationEnd_withInitialDelta_rotateToZeroDegree() {
+        mBaseBounds = new Rect(200, 200, 1_000, 1_000);
+        mStartBounds = new Rect(100, 100, 500, 500);
+        mEndBounds = new Rect(mBaseBounds);
+        final int duration = 10;
+        final float delta = 45;
+        final float[] matrix = new float[9];
+        mPipResizeAnimator = new PipResizeAnimator(mMockContext, mTestLeash,
+                mMockStartTransaction, mMockFinishTransaction,
+                mBaseBounds, mStartBounds, mEndBounds,
+                duration, delta);
+        mPipResizeAnimator.setSurfaceControlTransactionFactory(mMockFactory);
+
+        mPipResizeAnimator.setAnimationStartCallback(mMockStartCallback);
+        mPipResizeAnimator.setAnimationEndCallback(mMockEndCallback);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mPipResizeAnimator.start();
+            clearInvocations(mMockTransaction);
+            mPipResizeAnimator.end();
+        });
+
+        // Final animation transaction sets skew to zero
+        verify(mMockTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+        mArgumentCaptor.getValue().getValues(matrix);
+        assertEquals(matrix[Matrix.MSKEW_X], 0f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MSKEW_Y], 0f, FLOAT_COMPARISON_DELTA);
+
+        // Finish transaction sets skew to zero
+        verify(mMockFinishTransaction).setMatrix(eq(mTestLeash), mArgumentCaptor.capture(), any());
+        mArgumentCaptor.getValue().getValues(matrix);
+        assertEquals(matrix[Matrix.MSKEW_X], 0f, FLOAT_COMPARISON_DELTA);
+        assertEquals(matrix[Matrix.MSKEW_Y], 0f, FLOAT_COMPARISON_DELTA);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
index afdb687..efe4fb1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
@@ -34,6 +34,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.windowdecor.extension.isFullscreen
 import com.google.common.truth.Truth.assertThat
 import dagger.Lazy
 import org.junit.Before
@@ -107,8 +108,8 @@
         callOnTransitionFinished()
         executor.flushAll()
 
-        assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId)
-        assertThat(listener.taskInfoToBeNotified.windowingMode)
+        assertThat(listener.taskInfoOnTaskMovedToFront.taskId).isEqualTo(change.taskInfo?.taskId)
+        assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
             .isEqualTo(change.taskInfo?.windowingMode)
     }
 
@@ -130,8 +131,8 @@
         callOnTransitionFinished()
         executor.flushAll()
 
-        assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(1)
-        assertThat(listener.taskInfoToBeNotified.windowingMode)
+        assertThat(listener.taskInfoOnTaskMovedToFront.taskId).isEqualTo(1)
+        assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
             .isEqualTo(WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
     }
 
@@ -161,9 +162,9 @@
         callOnTransitionFinished()
         executor.flushAll()
 
-        assertThat(listener.taskInfoToBeNotified.taskId)
+        assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
             .isEqualTo(freeformOpenChange.taskInfo?.taskId)
-        assertThat(listener.taskInfoToBeNotified.windowingMode)
+        assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
             .isEqualTo(freeformOpenChange.taskInfo?.windowingMode)
     }
 
@@ -199,9 +200,15 @@
         callOnTransitionFinished()
         executor.flushAll()
 
-        assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId)
-        assertThat(listener.taskInfoToBeNotified.windowingMode)
+        assertThat(listener.taskInfoOnTaskMovedToFront.taskId).isEqualTo(change.taskInfo?.taskId)
+        assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
             .isEqualTo(change.taskInfo?.windowingMode)
+
+        assertThat(listener.taskInfoOnTaskChanged.size).isEqualTo(1)
+        with(listener.taskInfoOnTaskChanged.last()) {
+            assertThat(taskId).isEqualTo(mergedChange.taskInfo?.taskId)
+            assertThat(windowingMode).isEqualTo(mergedChange.taskInfo?.windowingMode)
+        }
     }
 
     @Test
@@ -236,18 +243,151 @@
         callOnTransitionFinished()
         executor.flushAll()
 
-        assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(mergedChange.taskInfo?.taskId)
-        assertThat(listener.taskInfoToBeNotified.windowingMode)
-                .isEqualTo(mergedChange.taskInfo?.windowingMode)
+        assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
+            .isEqualTo(mergedChange.taskInfo?.taskId)
+        assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
+            .isEqualTo(mergedChange.taskInfo?.windowingMode)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+    fun taskChange_freeformWindowToFullscreenWindow_listenerNotified() {
+        val listener = TestListener()
+        val executor = TestShellExecutor()
+        transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+        val freeformState =
+            createChange(
+                WindowManager.TRANSIT_OPEN,
+                createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+            )
+        val transitionInfoOpen =
+            TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(freeformState).build()
+        callOnTransitionReady(transitionInfoOpen)
+        callOnTransitionFinished()
+        executor.flushAll()
+
+        assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
+            .isEqualTo(freeformState.taskInfo?.taskId)
+        assertThat(listener.taskInfoOnTaskMovedToFront.windowingMode)
+            .isEqualTo(freeformState.taskInfo?.windowingMode)
+        assertThat(listener.taskInfoOnTaskMovedToFront.isFullscreen).isEqualTo(false)
+
+        // create change transition to update the windowing mode to full screen.
+        val fullscreenState =
+            createChange(
+                WindowManager.TRANSIT_CHANGE,
+                createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+            )
+        val transitionInfoChange =
+            TransitionInfoBuilder(WindowManager.TRANSIT_CHANGE, 0)
+                .addChange(fullscreenState)
+                .build()
+
+        callOnTransitionReady(transitionInfoChange)
+        callOnTransitionFinished()
+        executor.flushAll()
+
+        // Asserting whether freeformState remains the same as before the change
+        assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
+            .isEqualTo(freeformState.taskInfo?.taskId)
+        assertThat(listener.taskInfoOnTaskMovedToFront.isFullscreen).isEqualTo(false)
+
+        // Asserting changes
+        assertThat(listener.taskInfoOnTaskChanged.size).isEqualTo(1)
+        with(listener.taskInfoOnTaskChanged.last()) {
+            assertThat(taskId).isEqualTo(fullscreenState.taskInfo?.taskId)
+            assertThat(isFullscreen).isEqualTo(true)
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+    fun singleTransition_withOpenAndChange_onlyOpenIsNotified() {
+        val listener = TestListener()
+        val executor = TestShellExecutor()
+        transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+        // Creating multiple changes to be fired in a single transition
+        val freeformState =
+            createChange(
+                mode = WindowManager.TRANSIT_OPEN,
+                taskInfo = createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+            )
+        val fullscreenState =
+            createChange(
+                mode = WindowManager.TRANSIT_CHANGE,
+                taskInfo = createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+            )
+
+        val transitionInfoWithChanges =
+            TransitionInfoBuilder(WindowManager.TRANSIT_CHANGE, 0)
+                .addChange(freeformState)
+                .addChange(fullscreenState)
+                .build()
+
+        callOnTransitionReady(transitionInfoWithChanges)
+        callOnTransitionFinished()
+        executor.flushAll()
+
+        assertThat(listener.taskInfoOnTaskMovedToFront.taskId)
+            .isEqualTo(freeformState.taskInfo?.taskId)
+        assertThat(listener.taskInfoOnTaskMovedToFront.isFullscreen).isEqualTo(false)
+        assertThat(listener.taskInfoOnTaskChanged.size).isEqualTo(0)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+    fun singleTransition_withMultipleChanges_listenerNotified_forEachChange() {
+        val listener = TestListener()
+        val executor = TestShellExecutor()
+        transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+
+        val taskId = 1
+
+        // Creating multiple changes to be fired in a single transition
+        val changes =
+            listOf(
+                    WindowConfiguration.WINDOWING_MODE_FREEFORM,
+                    WindowConfiguration.WINDOW_CONFIG_DISPLAY_ROTATION,
+                    WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+                )
+                .map { change ->
+                    createChange(
+                        mode = WindowManager.TRANSIT_CHANGE,
+                        taskInfo = createTaskInfo(taskId, change)
+                    )
+                }
+
+        val transitionInfoWithChanges =
+            TransitionInfoBuilder(WindowManager.TRANSIT_CHANGE, 0)
+                .apply { changes.forEach { c -> this@apply.addChange(c) } }
+                .build()
+
+        callOnTransitionReady(transitionInfoWithChanges)
+        callOnTransitionFinished()
+        executor.flushAll()
+
+        assertThat(listener.taskInfoOnTaskChanged.size).isEqualTo(changes.size)
+        changes.forEachIndexed { index, change ->
+            assertThat(listener.taskInfoOnTaskChanged[index].taskId)
+                .isEqualTo(change.taskInfo?.taskId)
+            assertThat(listener.taskInfoOnTaskChanged[index].windowingMode)
+                .isEqualTo(change.taskInfo?.windowingMode)
+        }
     }
 
     class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener {
-        var taskInfoToBeNotified = ActivityManager.RunningTaskInfo()
+        var taskInfoOnTaskMovedToFront = ActivityManager.RunningTaskInfo()
+        var taskInfoOnTaskChanged = mutableListOf<ActivityManager.RunningTaskInfo>()
 
         override fun onTaskMovedToFrontThroughTransition(
             taskInfo: ActivityManager.RunningTaskInfo
         ) {
-            taskInfoToBeNotified = taskInfo
+            taskInfoOnTaskMovedToFront = taskInfo
+        }
+
+        override fun onTaskChangedThroughTransition(taskInfo: ActivityManager.RunningTaskInfo) {
+            taskInfoOnTaskChanged += taskInfo
         }
     }
 
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/util/WindowingEducationTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt
index 708fadb..99e8295 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt
@@ -32,11 +32,13 @@
     runningTaskInfo: RunningTaskInfo = createTaskInfo(),
     isHandleMenuExpanded: Boolean = false,
     globalAppHandleBounds: Rect = Rect(),
+    isCapturedLinkAvailable: Boolean = false
 ): CaptionState.AppHandle =
     CaptionState.AppHandle(
         runningTaskInfo = runningTaskInfo,
         isHandleMenuExpanded = isHandleMenuExpanded,
-        globalAppHandleBounds = globalAppHandleBounds)
+        globalAppHandleBounds = globalAppHandleBounds,
+        isCapturedLinkAvailable = isCapturedLinkAvailable)
 
 /**
  * Create an instance of [CaptionState.AppHeader] with parameters as properties.
@@ -47,11 +49,13 @@
     runningTaskInfo: RunningTaskInfo = createTaskInfo(),
     isHeaderMenuExpanded: Boolean = false,
     globalAppChipBounds: Rect = Rect(),
+    isCapturedLinkAvailable: Boolean = false
 ): CaptionState.AppHeader =
     CaptionState.AppHeader(
         runningTaskInfo = runningTaskInfo,
         isHeaderMenuExpanded = isHeaderMenuExpanded,
-        globalAppChipBounds = globalAppChipBounds)
+        globalAppChipBounds = globalAppChipBounds,
+        isCapturedLinkAvailable = isCapturedLinkAvailable)
 
 /**
  * Create an instance of [RunningTaskInfo] with parameters as properties.
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 c42be7f..03aab18 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
@@ -95,6 +95,7 @@
 import com.android.wm.shell.desktopmode.DesktopTasksLimiter
 import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository
 import com.android.wm.shell.desktopmode.education.AppHandleEducationController
+import com.android.wm.shell.desktopmode.education.AppToWebEducationController
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
@@ -195,6 +196,7 @@
             DesktopModeWindowDecorViewModel.TaskPositionerFactory
     @Mock private lateinit var mockTaskPositioner: TaskPositioner
     @Mock private lateinit var mockAppHandleEducationController: AppHandleEducationController
+    @Mock private lateinit var mockAppToWebEducationController: AppToWebEducationController
     @Mock private lateinit var mockFocusTransitionObserver: FocusTransitionObserver
     @Mock private lateinit var mockCaptionHandleRepository: WindowDecorCaptionHandleRepository
     @Mock private lateinit var motionEvent: MotionEvent
@@ -261,6 +263,7 @@
                 mockInteractionJankMonitor,
                 Optional.of(mockTasksLimiter),
                 mockAppHandleEducationController,
+                mockAppToWebEducationController,
                 mockCaptionHandleRepository,
                 Optional.of(mockActivityOrientationChangeHandler),
                 mockTaskPositionerFactory,
@@ -946,7 +949,7 @@
     }
 
     @Test
-    fun testDecor_onClickToSplitScreen_detachesStatusBarInputLayer() {
+    fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
         val toSplitScreenListenerCaptor = forClass(Function0::class.java)
                 as ArgumentCaptor<Function0<Unit>>
         val decor = createOpenTaskDecoration(
@@ -956,7 +959,7 @@
 
         toSplitScreenListenerCaptor.value.invoke()
 
-        verify(decor).detachStatusBarInputLayer()
+        verify(decor).disposeStatusBarInputLayer()
     }
 
     @Test
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 8a2c778..86ec675 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,6 +49,7 @@
 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;
@@ -849,7 +850,8 @@
         ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
         spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
-        verify(mMockHandler).post(runnableArgument.capture());
+        // Once for view host, the other for the AppHandle input layer.
+        verify(mMockHandler, times(2)).post(runnableArgument.capture());
         runnableArgument.getValue().run();
         verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
     }
@@ -876,7 +878,8 @@
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
         spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
-        verify(mMockHandler).post(runnableArgument.capture());
+        // Once for view host, the other for the AppHandle input layer.
+        verify(mMockHandler, times(2)).post(runnableArgument.capture());
 
         spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
@@ -890,7 +893,8 @@
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
         spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
-        verify(mMockHandler).post(runnableArgument.capture());
+        // Once for view host, the other for the AppHandle input layer.
+        verify(mMockHandler, times(2)).post(runnableArgument.capture());
 
         spyWindowDecor.close();
 
@@ -1295,7 +1299,8 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
+    @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION,
+            Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION})
     public void notifyCaptionStateChanged_flagDisabled_doNoNotify() {
         when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index cb7fade..8e0434c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -218,8 +218,6 @@
         verify(captionContainerSurfaceBuilder, never()).build();
         verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any());
 
-        verify(mMockSurfaceControlFinishT).hide(mMockTaskSurface);
-
         assertNull(mRelayoutResult.mRootView);
     }
 
@@ -281,8 +279,6 @@
 
         verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
         verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
-        verify(mMockSurfaceControlStartT)
-                .show(mMockTaskSurface);
         verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, 10);
 
         assertEquals(300, mRelayoutResult.mWidth);
@@ -863,7 +859,7 @@
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
 
-        mRelayoutParams.mSetTaskPositionAndCrop = false;
+        mRelayoutParams.mSetTaskVisibilityPositionAndCrop = false;
         windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockSurfaceControlStartT, never()).setWindowCrop(
@@ -891,7 +887,7 @@
         taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
         final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
 
-        mRelayoutParams.mSetTaskPositionAndCrop = true;
+        mRelayoutParams.mSetTaskVisibilityPositionAndCrop = true;
         windowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
 
         verify(mMockSurfaceControlStartT).setWindowCrop(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
index 741dfb8..15f2c7b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt
@@ -285,7 +285,7 @@
       onEducationClickAction: () -> Unit = {},
       onDismissAction: () -> Unit = {}
   ) =
-      DesktopWindowingEducationTooltipController.EducationViewConfig(
+      DesktopWindowingEducationTooltipController.TooltipEducationViewConfig(
           tooltipViewLayout = tooltipViewLayout,
           tooltipColorScheme = tooltipColorScheme,
           tooltipViewGlobalCoordinates = tooltipViewGlobalCoordinates,
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
index 0ccd424..80ad1df 100644
--- 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
@@ -36,9 +36,11 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -73,6 +75,7 @@
                 returnToDragStartAnimatorMock,
                 desktopRepository,
             )
+        whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
     }
 
     @Test
@@ -143,7 +146,7 @@
     }
 
     @Test
-    fun userChange_starting_allTilingSessionsShouldBeDestroyed() {
+    fun onUserChange_allTilingSessionsShouldBeDestroyed() {
         desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
             1,
             desktopTilingDecoration,
@@ -155,7 +158,29 @@
 
         desktopTilingDecorViewModel.onUserChange()
 
-        verify(desktopTilingDecoration, times(2)).onUserChange()
+        verify(desktopTilingDecoration, times(2)).resetTilingSession()
+    }
+
+    @Test
+    fun displayOrientationChange_tilingForDisplayShouldBeDestroyed() {
+        desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+            1,
+            desktopTilingDecoration,
+        )
+        desktopTilingDecorViewModel.tilingTransitionHandlerByDisplayId.put(
+            2,
+            desktopTilingDecoration,
+        )
+
+        desktopTilingDecorViewModel.onDisplayChange(1, 1, 2, null, null)
+
+        verify(desktopTilingDecoration, times(1)).resetTilingSession()
+        verify(displayControllerMock, times(1))
+            .addDisplayChangingController(eq(desktopTilingDecorViewModel))
+
+        desktopTilingDecorViewModel.onDisplayChange(1, 1, 3, null, null)
+        // No extra calls after 180 degree change.
+        verify(desktopTilingDecoration, times(1)).resetTilingSession()
     }
 
     companion object {
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
index 0ee3f46..3143946 100644
--- 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
@@ -16,9 +16,12 @@
 
 package com.android.wm.shell.windowdecor.tiling
 
+import android.content.Context
 import android.content.res.Configuration
 import android.graphics.Rect
 import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.RoundedCorner
 import android.view.SurfaceControl
 import androidx.test.annotation.UiThreadTest
 import androidx.test.filters.SmallTest
@@ -29,6 +32,7 @@
 import org.junit.Before
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
@@ -55,10 +59,17 @@
 
     private lateinit var desktopTilingWindowManager: DesktopTilingDividerWindowManager
 
+    private val context = mock<Context>()
+    private val display = mock<Display>()
+    private val roundedCorner = mock<RoundedCorner>()
+
     @Before
     fun setup() {
         config = Configuration()
         config.setToDefaults()
+        whenever(context.display).thenReturn(display)
+        whenever(display.getRoundedCorner(any())).thenReturn(roundedCorner)
+        whenever(roundedCorner.radius).thenReturn(CORNER_RADIUS)
         desktopTilingWindowManager =
             DesktopTilingDividerWindowManager(
                 config,
@@ -69,6 +80,7 @@
                 transitionHandlerMock,
                 transactionSupplierMock,
                 BOUNDS,
+                context,
             )
     }
 
@@ -85,7 +97,6 @@
 
         // 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())
@@ -93,7 +104,24 @@
         verify(transaction, times(1)).apply()
     }
 
+    @Test
+    @UiThreadTest
+    fun testWindowManager_accountsForRoundedCornerDimensions() {
+        whenever(transactionSupplierMock.get()).thenReturn(transaction)
+        whenever(transaction.setRelativeLayer(any(), any(), any())).thenReturn(transaction)
+        whenever(transaction.setRelativeLayer(any(), any(), any())).thenReturn(transaction)
+        whenever(transaction.setPosition(any(), any(), any())).thenReturn(transaction)
+        whenever(transaction.show(any())).thenReturn(transaction)
+
+        desktopTilingWindowManager.generateViewHost(surfaceControl)
+
+        // Ensure a surfaceControl transaction runs to show the divider.
+        verify(transaction, times(1))
+            .setPosition(any(), eq(BOUNDS.left.toFloat() - CORNER_RADIUS), any())
+    }
+
     companion object {
         private val BOUNDS = Rect(1, 2, 3, 4)
+        private val CORNER_RADIUS = 28
     }
 }
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
index 0b04a21..f371f52 100644
--- 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
@@ -49,6 +49,7 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Captor
 import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.capture
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
@@ -112,6 +113,7 @@
                 returnToDragStartAnimator,
                 desktopRepository,
             )
+        whenever(context.createContextAsUser(any(), any())).thenReturn(context)
     }
 
     @Test
@@ -195,7 +197,7 @@
 
         verify(toggleResizeDesktopTaskTransitionHandler, times(1))
             .startTransition(capture(wctCaptor), any())
-        verify(returnToDragStartAnimator, times(1)).start(any(), any(), any(), any(), any())
+        verify(returnToDragStartAnimator, times(1)).start(any(), any(), any(), any(), anyOrNull())
         for (change in wctCaptor.value.changes) {
             val bounds = change.value.configuration.windowConfiguration.bounds
             val leftBounds = getLeftTaskBounds()
@@ -473,12 +475,13 @@
         tilingDecoration.rightTaskResizingHelper = tiledTaskHelper
         tilingDecoration.desktopTilingDividerWindowManager = desktopTilingDividerWindowManager
 
-        tilingDecoration.onUserChange()
+        tilingDecoration.resetTilingSession()
 
         assertThat(tilingDecoration.leftTaskResizingHelper).isNull()
         assertThat(tilingDecoration.rightTaskResizingHelper).isNull()
         verify(desktopWindowDecoration, times(2)).removeDragResizeListener(any())
         verify(tiledTaskHelper, times(2)).dispose()
+        verify(context, never()).getApplicationContext()
     }
 
     private fun initTiledTaskHelperMock(taskInfo: ActivityManager.RunningTaskInfo) {
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
index fd5eb88..c96ce95 100644
--- 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
@@ -32,8 +32,10 @@
 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)
@@ -68,6 +70,7 @@
         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)
@@ -79,6 +82,24 @@
         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
diff --git a/libs/androidfw/PngCrunch.cpp b/libs/androidfw/PngCrunch.cpp
index cf3c0ee..e945405 100644
--- a/libs/androidfw/PngCrunch.cpp
+++ b/libs/androidfw/PngCrunch.cpp
@@ -506,8 +506,7 @@
   // Set up the write functions which write to our custom data sources.
   png_set_write_fn(write_ptr, (png_voidp)out, WriteDataToStream, nullptr);
 
-  // We want small files and can take the performance hit to achieve this goal.
-  png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
+  png_set_compression_level(write_ptr, options.compression_level);
 
   // Begin analysis of the image data.
   // Scan the entire image and determine if:
diff --git a/libs/androidfw/include/androidfw/Png.h b/libs/androidfw/include/androidfw/Png.h
index 2ece43e..72be59b 100644
--- a/libs/androidfw/include/androidfw/Png.h
+++ b/libs/androidfw/include/androidfw/Png.h
@@ -31,6 +31,8 @@
 
 struct PngOptions {
   int grayscale_tolerance = 0;
+  // By default we want small files and can take the performance hit to achieve this goal.
+  int compression_level = 9;
 };
 
 /**
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index 27817e9..faf84a8 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -3,8 +3,9 @@
 
   public final class AppFunctionManager {
     ctor public AppFunctionManager(android.content.Context);
-    method public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
-    method public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
+    method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+    method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
+    method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
     method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
     field public static final int APP_FUNCTION_STATE_DEFAULT = 0; // 0x0
     field public static final int APP_FUNCTION_STATE_DISABLED = 2; // 0x2
@@ -34,6 +35,7 @@
   }
 
   public final class ExecuteAppFunctionResponse {
+    method public int getErrorCategory();
     method @Nullable public String getErrorMessage();
     method @NonNull public android.os.Bundle getExtras();
     method public int getResultCode();
@@ -41,14 +43,19 @@
     method public boolean isSuccess();
     method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
     method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
+    field public static final int ERROR_CATEGORY_APP = 3; // 0x3
+    field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1
+    field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2
+    field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0
     field public static final String PROPERTY_RETURN_VALUE = "returnValue";
-    field public static final int RESULT_APP_UNKNOWN_ERROR = 2; // 0x2
-    field public static final int RESULT_CANCELLED = 6; // 0x6
-    field public static final int RESULT_DENIED = 1; // 0x1
-    field public static final int RESULT_DISABLED = 5; // 0x5
-    field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
-    field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
+    field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8
+    field public static final int RESULT_CANCELLED = 2001; // 0x7d1
+    field public static final int RESULT_DENIED = 1000; // 0x3e8
+    field public static final int RESULT_DISABLED = 1002; // 0x3ea
+    field public static final int RESULT_FUNCTION_NOT_FOUND = 1003; // 0x3eb
+    field public static final int RESULT_INVALID_ARGUMENT = 1001; // 0x3e9
     field public static final int RESULT_OK = 0; // 0x0
+    field public static final int RESULT_SYSTEM_ERROR = 2000; // 0x7d0
   }
 
 }
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..2075104 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
@@ -20,6 +20,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.UserHandleAware;
 import android.content.Context;
@@ -41,8 +42,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 +69,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,7 +101,16 @@
      * <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.
      */
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+                Manifest.permission.EXECUTE_APP_FUNCTIONS
+            },
+            conditional = true)
     public void executeAppFunction(
             @NonNull ExecuteAppFunctionRequest sidecarRequest,
             @NonNull @CallbackExecutor Executor executor,
@@ -128,25 +136,15 @@
     /**
      * 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.
      */
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+                Manifest.permission.EXECUTE_APP_FUNCTIONS
+            },
+            conditional = true)
     public void isAppFunctionEnabled(
             @NonNull String functionIdentifier,
             @NonNull String targetPackage,
@@ -156,22 +154,23 @@
     }
 
     /**
+     * Returns a boolean through a callback, indicating whether the app function is enabled.
+     *
+     * <p>See {@link android.app.appfunctions.AppFunctionManager#isAppFunctionEnabled} for the
+     * documented behaviour of this method.
+     */
+    public void isAppFunctionEnabled(
+            @NonNull String functionIdentifier,
+            @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+        mManager.isAppFunctionEnabled(functionIdentifier, executor, callback);
+    }
+
+    /**
      * 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..4e88fb0 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java
@@ -50,37 +50,102 @@
      */
     public static final String PROPERTY_RETURN_VALUE = "returnValue";
 
-    /** The call was successful. */
+    /**
+     * The call was successful.
+     *
+     * <p>This result code does not belong in an error category.
+     */
     public static final int RESULT_OK = 0;
 
-    /** 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. */
-    public static final int RESULT_APP_UNKNOWN_ERROR = 2;
-
     /**
-     * An internal error occurred within AppFunctionManagerService.
+     * The caller does not have the permission to execute an app function.
      *
-     * <p>This error may be considered similar to {@link IllegalStateException}
+     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
      */
-    public static final int RESULT_INTERNAL_ERROR = 3;
+    public static final int RESULT_DENIED = 1000;
 
     /**
-     * The caller supplied invalid arguments to the call.
+     * The caller supplied invalid arguments to the execution request.
      *
      * <p>This error may be considered similar to {@link IllegalArgumentException}.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
      */
-    public static final int RESULT_INVALID_ARGUMENT = 4;
+    public static final int RESULT_INVALID_ARGUMENT = 1001;
 
-    /** The caller tried to execute a disabled app function. */
-    public static final int RESULT_DISABLED = 5;
+    /**
+     * The caller tried to execute a disabled app function.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+     */
+    public static final int RESULT_DISABLED = 1002;
+
+    /**
+     * The caller tried to execute a function that does not exist.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category.
+     */
+    public static final int RESULT_FUNCTION_NOT_FOUND = 1003;
+
+    /**
+     * An internal unexpected error coming from the system.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
+     */
+    public static final int RESULT_SYSTEM_ERROR = 2000;
 
     /**
      * The operation was cancelled. Use this error code to report that a cancellation is done after
      * receiving a cancellation signal.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category.
      */
-    public static final int RESULT_CANCELLED = 6;
+    public static final int RESULT_CANCELLED = 2001;
+
+    /**
+     * 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.
+     *
+     * <p>This error is in the {@link #ERROR_CATEGORY_APP} category.
+     */
+    public static final int RESULT_APP_UNKNOWN_ERROR = 3000;
+
+    /**
+     * The error category is unknown.
+     *
+     * <p>This is the default value for {@link #getErrorCategory}.
+     */
+    public static final int ERROR_CATEGORY_UNKNOWN = 0;
+
+    /**
+     * The error is caused by the app requesting a function execution.
+     *
+     * <p>For example, the caller provided invalid parameters in the execution request e.g. an
+     * invalid function ID.
+     *
+     * <p>Errors in the category fall in the range 1000-1999 inclusive.
+     */
+    public static final int ERROR_CATEGORY_REQUEST_ERROR = 1;
+
+    /**
+     * The error is caused by an issue in the system.
+     *
+     * <p>For example, the AppFunctionService implementation is not found by the system.
+     *
+     * <p>Errors in the category fall in the range 2000-2999 inclusive.
+     */
+    public static final int ERROR_CATEGORY_SYSTEM = 2;
+
+    /**
+     * The error is caused by the app providing the function.
+     *
+     * <p>For example, the app crashed when the system is executing the request.
+     *
+     * <p>Errors in the category fall in the range 3000-3999 inclusive.
+     */
+    public static final int ERROR_CATEGORY_APP = 3;
 
     /** The result code of the app function execution. */
     @ResultCode private final int mResultCode;
@@ -170,6 +235,36 @@
     }
 
     /**
+     * Returns the error category of the {@link ExecuteAppFunctionResponse}.
+     *
+     * <p>This method categorizes errors based on their underlying cause, allowing developers to
+     * implement targeted error handling and provide more informative error messages to users. It
+     * maps ranges of result codes to specific error categories.
+     *
+     * <p>When constructing a {@link #newFailure} response, use the appropriate result code value to
+     * ensure correct categorization of the failed response.
+     *
+     * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the result code does not belong to
+     * any error category, for example, in the case of a successful result with {@link #RESULT_OK}.
+     *
+     * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding
+     * result code ranges.
+     */
+    @ErrorCategory
+    public int getErrorCategory() {
+        if (mResultCode >= 1000 && mResultCode < 2000) {
+            return ERROR_CATEGORY_REQUEST_ERROR;
+        }
+        if (mResultCode >= 2000 && mResultCode < 3000) {
+            return ERROR_CATEGORY_SYSTEM;
+        }
+        if (mResultCode >= 3000 && mResultCode < 4000) {
+            return ERROR_CATEGORY_APP;
+        }
+        return ERROR_CATEGORY_UNKNOWN;
+    }
+
+    /**
      * Returns a generic document containing the return value of the executed function.
      *
      * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
@@ -237,11 +332,28 @@
                 RESULT_OK,
                 RESULT_DENIED,
                 RESULT_APP_UNKNOWN_ERROR,
-                RESULT_INTERNAL_ERROR,
+                RESULT_SYSTEM_ERROR,
+                RESULT_FUNCTION_NOT_FOUND,
                 RESULT_INVALID_ARGUMENT,
                 RESULT_DISABLED,
                 RESULT_CANCELLED
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ResultCode {}
+
+    /**
+     * Error categories.
+     *
+     * @hide
+     */
+    @IntDef(
+            prefix = {"ERROR_CATEGORY_"},
+            value = {
+                ERROR_CATEGORY_UNKNOWN,
+                ERROR_CATEGORY_REQUEST_ERROR,
+                ERROR_CATEGORY_APP,
+                ERROR_CATEGORY_SYSTEM
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ErrorCategory {}
 }
diff --git a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt b/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
index 1f9fddd..264f842 100644
--- a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
+++ b/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt
@@ -105,7 +105,7 @@
         val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build()
         val platformResponse =
             ExecuteAppFunctionResponse.newFailure(
-                ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+                ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
                 null,
                 null
             )
@@ -119,7 +119,7 @@
         assertThat(sidecarResponse.resultDocument.id).isEqualTo(emptyGd.id)
         assertThat(sidecarResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType)
         assertThat(sidecarResponse.resultCode)
-            .isEqualTo(ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR)
+            .isEqualTo(ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR)
         assertThat(sidecarResponse.errorMessage).isNull()
     }
 
@@ -152,7 +152,7 @@
         val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build()
         val sidecarResponse =
             com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse.newFailure(
-                ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+                ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
                 null,
                 null
             )
@@ -166,7 +166,7 @@
         assertThat(platformResponse.resultDocument.id).isEqualTo(emptyGd.id)
         assertThat(platformResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType)
         assertThat(platformResponse.resultCode)
-            .isEqualTo(ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR)
+            .isEqualTo(ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR)
         assertThat(platformResponse.errorMessage).isNull()
     }
 }
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 266c236..fcb7efc 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -384,6 +384,7 @@
         "jni/ScopedParcel.cpp",
         "jni/Shader.cpp",
         "jni/RenderEffect.cpp",
+        "jni/RuntimeEffectUtils.cpp",
         "jni/Typeface.cpp",
         "jni/Utils.cpp",
         "jni/YuvToJpegEncoder.cpp",
@@ -579,6 +580,7 @@
         "utils/Color.cpp",
         "utils/LinearAllocator.cpp",
         "utils/StringUtils.cpp",
+        "utils/StatsUtils.cpp",
         "utils/TypefaceUtils.cpp",
         "utils/VectorDrawableUtils.cpp",
         "AnimationContext.cpp",
diff --git a/libs/hwui/ColorFilter.h b/libs/hwui/ColorFilter.h
index 3a3bfb47..f6b6be0 100644
--- a/libs/hwui/ColorFilter.h
+++ b/libs/hwui/ColorFilter.h
@@ -22,6 +22,7 @@
 #include <memory>
 
 #include "GraphicsJNI.h"
+#include "RuntimeEffectUtils.h"
 #include "SkColorFilter.h"
 
 namespace android {
@@ -113,6 +114,36 @@
     std::vector<float> mMatrix;
 };
 
+class RuntimeColorFilter : public ColorFilter {
+public:
+    RuntimeColorFilter(SkRuntimeEffectBuilder* builder) : mBuilder(builder) {}
+
+    void updateUniforms(JNIEnv* env, const char* name, const float vals[], int count,
+                        bool isColor) {
+        UpdateFloatUniforms(env, mBuilder, name, vals, count, isColor);
+        discardInstance();
+    }
+
+    void updateUniforms(JNIEnv* env, const char* name, const int vals[], int count) {
+        UpdateIntUniforms(env, mBuilder, name, vals, count);
+        discardInstance();
+    }
+
+    void updateChild(JNIEnv* env, const char* name, SkFlattenable* childEffect) {
+        UpdateChild(env, mBuilder, name, childEffect);
+        discardInstance();
+    }
+
+private:
+    sk_sp<SkColorFilter> createInstance() override {
+        // TODO: throw error if null
+        return mBuilder->makeColorFilter();
+    }
+
+private:
+    SkRuntimeEffectBuilder* mBuilder;
+};
+
 }  // namespace uirenderer
 }  // namespace android
 
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 93df478..5ad788c 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -139,3 +139,18 @@
     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"
+}
+
+flag {
+  name: "animated_image_drawable_filter_bitmap"
+  is_exported: true
+  namespace: "core_graphics"
+  description: "API's that enable animated image drawables to use nearest sampling when scaling."
+  bug: "370523334"
+}
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 69613c7..5e379aa 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -347,4 +347,26 @@
     return adjustFrameDuration(mSkAnimatedImage->currentFrameDuration());
 }
 
+bool AnimatedImageDrawable::getFilterBitmap() const {
+    const SkFilterMode kFilterBitmap = mSkAnimatedImage->getFilterMode();
+    if (kFilterBitmap == SkFilterMode::kLinear) {
+        return true;
+    }
+    return false;
+}
+
+bool AnimatedImageDrawable::setFilterBitmap(bool filterBitmap) {
+    if (filterBitmap) {
+        if (mSkAnimatedImage->getFilterMode() == SkFilterMode::kLinear) {
+            return false;
+        }
+        mSkAnimatedImage->setFilterMode(SkFilterMode::kLinear);
+    } else {
+        if (mSkAnimatedImage->getFilterMode() == SkFilterMode::kNearest) {
+            return false;
+        }
+        mSkAnimatedImage->setFilterMode(SkFilterMode::kNearest);
+    }
+    return true;
+}
 }  // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 1e965ab..2212324 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -87,6 +87,11 @@
     bool isRunning();
     int getRepetitionCount() const { return mSkAnimatedImage->getRepetitionCount(); }
     void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
+    // Returns true if the filter mode is set to linear sampling; false if it is
+    // set to nearest neighbor sampling.
+    bool getFilterBitmap() const;
+    // Returns true if the filter mode was changed; false otherwise.
+    bool setFilterBitmap(bool filterBitmap);
 
     void setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener> listener) {
         mEndListener = std::move(listener);
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index b73380e..cc292d9 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
@@ -46,16 +47,27 @@
 #include <SkImage.h>
 #include <SkImageAndroid.h>
 #include <SkImagePriv.h>
+#include <SkJpegEncoder.h>
 #include <SkJpegGainmapEncoder.h>
 #include <SkPixmap.h>
+#include <SkPngEncoder.h>
 #include <SkRect.h>
 #include <SkStream.h>
-#include <SkJpegEncoder.h>
-#include <SkPngEncoder.h>
 #include <SkWebpEncoder.h>
 
+#include <atomic>
+#include <format>
 #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 +98,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 +151,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 +172,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 +190,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 +311,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 +321,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 +347,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 +633,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/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index e074a27..a9a5db8 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -27,8 +27,8 @@
 #include <SkColorSpace.h>
 #include <SkColorType.h>
 #include <SkEncodedOrigin.h>
-#include <SkImageInfo.h>
 #include <SkGainmapInfo.h>
+#include <SkImageInfo.h>
 #include <SkMatrix.h>
 #include <SkPaint.h>
 #include <SkPngChunkReader.h>
@@ -43,6 +43,8 @@
 
 #include <memory>
 
+#include "modules/skcms/src/skcms_public.h"
+
 using namespace android;
 
 sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const {
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index b01e38d..2c8530d 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -276,6 +276,18 @@
     drawable->setStagingBounds(rect);
 }
 
+static jboolean AnimatedImageDrawable_nSetFilterBitmap(JNIEnv* env, jobject /*clazz*/,
+                                                       jlong nativePtr, jboolean filterBitmap) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    return drawable->setFilterBitmap(filterBitmap);
+}
+
+static jboolean AnimatedImageDrawable_nGetFilterBitmap(JNIEnv* env, jobject /*clazz*/,
+                                                       jlong nativePtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    return drawable->getFilterBitmap();
+}
+
 static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
         {"nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",
          (void*)AnimatedImageDrawable_nCreate},
@@ -294,6 +306,8 @@
         {"nNativeByteSize", "(J)J", (void*)AnimatedImageDrawable_nNativeByteSize},
         {"nSetMirrored", "(JZ)V", (void*)AnimatedImageDrawable_nSetMirrored},
         {"nSetBounds", "(JLandroid/graphics/Rect;)V", (void*)AnimatedImageDrawable_nSetBounds},
+        {"nSetFilterBitmap", "(JZ)Z", (void*)AnimatedImageDrawable_nSetFilterBitmap},
+        {"nGetFilterBitmap", "(J)Z", (void*)AnimatedImageDrawable_nGetFilterBitmap},
 };
 
 int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
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/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index 49a7f73..8b43f1d 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -10,6 +10,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <sys/stat.h>
+#include <utils/StatsUtils.h>
 
 #include <memory>
 
@@ -630,6 +631,7 @@
         }
         bitmap::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
         outputBitmap.notifyPixelsChanged();
+        uirenderer::logBitmapDecode(*reuseBitmap);
         // If a java bitmap was passed in for reuse, pass it back
         return javaBitmap;
     }
@@ -650,6 +652,7 @@
             }
         }
 
+        uirenderer::logBitmapDecode(*hardwareBitmap);
         return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags,
                 ninePatchChunk, ninePatchInsets, -1);
     }
@@ -659,6 +662,7 @@
         heapBitmap->setGainmap(std::move(gainmap));
     }
 
+    uirenderer::logBitmapDecode(*heapBitmap);
     // now create the java bitmap
     return bitmap::createBitmap(env, heapBitmap, bitmapCreateFlags, ninePatchChunk, ninePatchInsets,
                                 -1);
diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp
index f7e8e07..5ffd5b9 100644
--- a/libs/hwui/jni/BitmapRegionDecoder.cpp
+++ b/libs/hwui/jni/BitmapRegionDecoder.cpp
@@ -19,6 +19,7 @@
 #include <HardwareBitmapUploader.h>
 #include <androidfw/Asset.h>
 #include <sys/stat.h>
+#include <utils/StatsUtils.h>
 
 #include <memory>
 
@@ -376,6 +377,7 @@
             recycledBitmap->setGainmap(std::move(gainmap));
         }
         bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
+        uirenderer::logBitmapDecode(*recycledBitmap);
         return javaBitmap;
     }
 
@@ -392,12 +394,14 @@
                 hardwareBitmap->setGainmap(std::move(gm));
             }
         }
+        uirenderer::logBitmapDecode(*hardwareBitmap);
         return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags);
     }
     Bitmap* heapBitmap = heapAlloc.getStorageObjAndReset();
     if (hasGainmap && heapBitmap != nullptr) {
         heapBitmap->setGainmap(std::move(gainmap));
     }
+    uirenderer::logBitmapDecode(*heapBitmap);
     return android::bitmap::createBitmap(env, heapBitmap, bitmapCreateFlags);
 }
 
diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp
index 0b95148..20301d2 100644
--- a/libs/hwui/jni/ColorFilter.cpp
+++ b/libs/hwui/jni/ColorFilter.cpp
@@ -18,7 +18,9 @@
 #include "ColorFilter.h"
 
 #include "GraphicsJNI.h"
+#include "RuntimeEffectUtils.h"
 #include "SkBlendMode.h"
+#include "include/effects/SkRuntimeEffect.h"
 
 namespace android {
 
@@ -89,6 +91,78 @@
             filter->setMatrix(getMatrixFromJFloatArray(env, jarray));
         }
     }
+
+    static jlong RuntimeColorFilter_createColorFilter(JNIEnv* env, jobject, jstring agsl) {
+        ScopedUtfChars strSksl(env, agsl);
+        auto result = SkRuntimeEffect::MakeForColorFilter(SkString(strSksl.c_str()),
+                                                          SkRuntimeEffect::Options{});
+        if (result.effect.get() == nullptr) {
+            doThrowIAE(env, result.errorText.c_str());
+            return 0;
+        }
+        auto builder = new SkRuntimeEffectBuilder(std::move(result.effect));
+        auto* runtimeColorFilter = new RuntimeColorFilter(builder);
+        runtimeColorFilter->incStrong(nullptr);
+        return static_cast<jlong>(reinterpret_cast<uintptr_t>(runtimeColorFilter));
+    }
+
+    static void RuntimeColorFilter_updateUniformsFloatArray(JNIEnv* env, jobject,
+                                                            jlong colorFilterPtr,
+                                                            jstring uniformName,
+                                                            jfloatArray uniforms,
+                                                            jboolean isColor) {
+        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+        ScopedUtfChars name(env, uniformName);
+        AutoJavaFloatArray autoValues(env, uniforms, 0, kRO_JNIAccess);
+        if (filter) {
+            filter->updateUniforms(env, name.c_str(), autoValues.ptr(), autoValues.length(),
+                                   isColor);
+        }
+    }
+
+    static void RuntimeColorFilter_updateUniformsFloats(JNIEnv* env, jobject, jlong colorFilterPtr,
+                                                        jstring uniformName, jfloat value1,
+                                                        jfloat value2, jfloat value3, jfloat value4,
+                                                        jint count) {
+        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+        ScopedUtfChars name(env, uniformName);
+        const float values[4] = {value1, value2, value3, value4};
+        if (filter) {
+            filter->updateUniforms(env, name.c_str(), values, count, false);
+        }
+    }
+
+    static void RuntimeColorFilter_updateUniformsIntArray(JNIEnv* env, jobject,
+                                                          jlong colorFilterPtr, jstring uniformName,
+                                                          jintArray uniforms) {
+        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+        ScopedUtfChars name(env, uniformName);
+        AutoJavaIntArray autoValues(env, uniforms, 0);
+        if (filter) {
+            filter->updateUniforms(env, name.c_str(), autoValues.ptr(), autoValues.length());
+        }
+    }
+
+    static void RuntimeColorFilter_updateUniformsInts(JNIEnv* env, jobject, jlong colorFilterPtr,
+                                                      jstring uniformName, jint value1, jint value2,
+                                                      jint value3, jint value4, jint count) {
+        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+        ScopedUtfChars name(env, uniformName);
+        const int values[4] = {value1, value2, value3, value4};
+        if (filter) {
+            filter->updateUniforms(env, name.c_str(), values, count);
+        }
+    }
+
+    static void RuntimeColorFilter_updateChild(JNIEnv* env, jobject, jlong colorFilterPtr,
+                                               jstring childName, jlong childPtr) {
+        auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr);
+        ScopedUtfChars name(env, childName);
+        auto* child = reinterpret_cast<SkFlattenable*>(childPtr);
+        if (filter && child) {
+            filter->updateChild(env, name.c_str(), child);
+        }
+    }
 };
 
 static const JNINativeMethod colorfilter_methods[] = {
@@ -107,6 +181,20 @@
         {"nativeColorMatrixFilter", "([F)J", (void*)ColorFilterGlue::CreateColorMatrixFilter},
         {"nativeSetColorMatrix", "(J[F)V", (void*)ColorFilterGlue::SetColorMatrix}};
 
+static const JNINativeMethod runtime_color_filter_methods[] = {
+        {"nativeCreateRuntimeColorFilter", "(Ljava/lang/String;)J",
+         (void*)ColorFilterGlue::RuntimeColorFilter_createColorFilter},
+        {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
+         (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsFloatArray},
+        {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
+         (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsFloats},
+        {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
+         (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsIntArray},
+        {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
+         (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsInts},
+        {"nativeUpdateChild", "(JLjava/lang/String;J)V",
+         (void*)ColorFilterGlue::RuntimeColorFilter_updateChild}};
+
 int register_android_graphics_ColorFilter(JNIEnv* env) {
     android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods,
                                   NELEM(colorfilter_methods));
@@ -118,7 +206,10 @@
                                   NELEM(lighting_methods));
     android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter",
                                   colormatrix_methods, NELEM(colormatrix_methods));
-    
+    android::RegisterMethodsOrDie(env, "android/graphics/RuntimeColorFilter",
+                                  runtime_color_filter_methods,
+                                  NELEM(runtime_color_filter_methods));
+
     return 0;
 }
 
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index aebc4db..90fd3d8 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -37,6 +37,7 @@
 #include <hwui/Bitmap.h>
 #include <hwui/ImageDecoder.h>
 #include <sys/stat.h>
+#include <utils/StatsUtils.h>
 
 #include "Bitmap.h"
 #include "BitmapFactory.h"
@@ -485,6 +486,7 @@
                         hwBitmap->setGainmap(std::move(gm));
                     }
                 }
+                uirenderer::logBitmapDecode(*hwBitmap);
                 return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
                                             ninePatchChunk, ninePatchInsets);
             }
@@ -498,6 +500,8 @@
 
         nativeBitmap->setImmutable();
     }
+
+    uirenderer::logBitmapDecode(*nativeBitmap);
     return bitmap::createBitmap(env, nativeBitmap.release(), bitmapCreateFlags, ninePatchChunk,
                                 ninePatchInsets);
 }
diff --git a/libs/hwui/jni/RuntimeEffectUtils.cpp b/libs/hwui/jni/RuntimeEffectUtils.cpp
new file mode 100644
index 0000000..46db863
--- /dev/null
+++ b/libs/hwui/jni/RuntimeEffectUtils.cpp
@@ -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.
+ */
+
+#include "RuntimeEffectUtils.h"
+
+#include "include/effects/SkRuntimeEffect.h"
+
+namespace android {
+namespace uirenderer {
+
+static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
+    va_end(args);
+    return ret;
+}
+
+bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
+    switch (type) {
+        case SkRuntimeEffect::Uniform::Type::kFloat:
+        case SkRuntimeEffect::Uniform::Type::kFloat2:
+        case SkRuntimeEffect::Uniform::Type::kFloat3:
+        case SkRuntimeEffect::Uniform::Type::kFloat4:
+        case SkRuntimeEffect::Uniform::Type::kFloat2x2:
+        case SkRuntimeEffect::Uniform::Type::kFloat3x3:
+        case SkRuntimeEffect::Uniform::Type::kFloat4x4:
+            return false;
+        case SkRuntimeEffect::Uniform::Type::kInt:
+        case SkRuntimeEffect::Uniform::Type::kInt2:
+        case SkRuntimeEffect::Uniform::Type::kInt3:
+        case SkRuntimeEffect::Uniform::Type::kInt4:
+            return true;
+    }
+}
+
+void UpdateFloatUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+                         const float values[], int count, bool isColor) {
+    SkRuntimeEffectBuilder::BuilderUniform uniform = builder->uniform(uniformName);
+    if (uniform.fVar == nullptr) {
+        ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+    } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
+        if (isColor) {
+            jniThrowExceptionFmt(
+                    env, "java/lang/IllegalArgumentException",
+                    "attempting to set a color uniform using the non-color specific APIs: %s %x",
+                    uniformName, uniform.fVar->flags);
+        } else {
+            ThrowIAEFmt(env,
+                        "attempting to set a non-color uniform using the setColorUniform APIs: %s",
+                        uniformName);
+        }
+    } else if (isIntUniformType(uniform.fVar->type)) {
+        ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
+                    uniformName);
+    } else if (!uniform.set<float>(values, count)) {
+        ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+                    uniform.fVar->sizeInBytes(), sizeof(float) * count);
+    }
+}
+
+void UpdateIntUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+                       const int values[], int count) {
+    SkRuntimeEffectBuilder::BuilderUniform uniform = builder->uniform(uniformName);
+    if (uniform.fVar == nullptr) {
+        ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
+    } else if (!isIntUniformType(uniform.fVar->type)) {
+        ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
+                    uniformName);
+    } else if (!uniform.set<int>(values, count)) {
+        ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
+                    uniform.fVar->sizeInBytes(), sizeof(float) * count);
+    }
+}
+
+void UpdateChild(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* childName,
+                 SkFlattenable* childEffect) {
+    SkRuntimeShaderBuilder::BuilderChild builderChild = builder->child(childName);
+    if (builderChild.fChild == nullptr) {
+        ThrowIAEFmt(env, "unable to find shader named %s", childName);
+        return;
+    }
+
+    builderChild = sk_ref_sp(childEffect);
+}
+
+}  // namespace uirenderer
+}  // namespace android
\ No newline at end of file
diff --git a/libs/hwui/jni/RuntimeEffectUtils.h b/libs/hwui/jni/RuntimeEffectUtils.h
new file mode 100644
index 0000000..75623c0
--- /dev/null
+++ b/libs/hwui/jni/RuntimeEffectUtils.h
@@ -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.
+ */
+
+#ifndef RUNTIMEEFFECTUTILS_H
+#define RUNTIMEEFFECTUTILS_H
+
+#include "GraphicsJNI.h"
+#include "include/effects/SkRuntimeEffect.h"
+
+namespace android {
+namespace uirenderer {
+
+void UpdateFloatUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+                         const float values[], int count, bool isColor);
+
+void UpdateIntUniforms(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* uniformName,
+                       const int values[], int count);
+
+void UpdateChild(JNIEnv* env, SkRuntimeEffectBuilder* builder, const char* childName,
+                 SkFlattenable* childEffect);
+}  // namespace uirenderer
+}  // namespace android
+
+#endif  // MAIN_RUNTIMEEFFECTUTILS_H
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 6e03bbd..d9dc8eb 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -593,9 +593,9 @@
 
             Matrix4 transform;
             SkIRect clipBounds;
+            uirenderer::Rect initialClipBounds;
+            const auto clipFlags = props.getClippingFlags();
             if (enableClip) {
-                uirenderer::Rect initialClipBounds;
-                const auto clipFlags = props.getClippingFlags();
                 if (clipFlags) {
                     props.getClippingRectForFlags(clipFlags, &initialClipBounds);
                 } else {
@@ -659,8 +659,8 @@
                         static_cast<jint>(bounds.left), static_cast<jint>(bounds.top),
                         static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom),
                         static_cast<jint>(clipBounds.fLeft), static_cast<jint>(clipBounds.fTop),
-                        static_cast<jint>(clipBounds.fRight),
-                        static_cast<jint>(clipBounds.fBottom));
+                        static_cast<jint>(clipBounds.fRight), static_cast<jint>(clipBounds.fBottom),
+                        static_cast<jint>(props.getWidth()), static_cast<jint>(props.getHeight()));
             }
             if (!keepListening) {
                 env->DeleteGlobalRef(mListener);
@@ -891,7 +891,7 @@
     gPositionListener.callPositionChanged = GetStaticMethodIDOrDie(
             env, clazz, "callPositionChanged", "(Ljava/lang/ref/WeakReference;JIIII)Z");
     gPositionListener.callPositionChanged2 = GetStaticMethodIDOrDie(
-            env, clazz, "callPositionChanged2", "(Ljava/lang/ref/WeakReference;JIIIIIIII)Z");
+            env, clazz, "callPositionChanged2", "(Ljava/lang/ref/WeakReference;JIIIIIIIIII)Z");
     gPositionListener.callApplyStretch = GetStaticMethodIDOrDie(
             env, clazz, "callApplyStretch", "(Ljava/lang/ref/WeakReference;JFFFFFFFFFF)Z");
     gPositionListener.callPositionLost = GetStaticMethodIDOrDie(
diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt
index 2414299..b559194 100644
--- a/libs/hwui/libhwui.map.txt
+++ b/libs/hwui/libhwui.map.txt
@@ -67,6 +67,7 @@
       SkFILEStream::SkFILEStream*;
       SkImageInfo::*;
       SkMemoryStream::SkMemoryStream*;
+      android::uirenderer::logBitmapDecode*;
     };
   local:
     *;
diff --git a/libs/hwui/platform/host/android/api-level.h b/libs/hwui/platform/host/android/api-level.h
deleted file mode 120000
index 4fb4784..0000000
--- a/libs/hwui/platform/host/android/api-level.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../bionic/libc/include/android/api-level.h
\ No newline at end of file
diff --git a/libs/hwui/utils/StatsUtils.cpp b/libs/hwui/utils/StatsUtils.cpp
new file mode 100644
index 0000000..5c4027e
--- /dev/null
+++ b/libs/hwui/utils/StatsUtils.cpp
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#ifdef __ANDROID__
+#include <dlfcn.h>
+#include <log/log.h>
+#include <statslog_hwui.h>
+#include <statssocket_lazy.h>
+#include <utils/Errors.h>
+
+#include <mutex>
+#endif
+
+#include <unistd.h>
+
+#include "StatsUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+#ifdef __ANDROID__
+
+namespace {
+
+int32_t toStatsColorSpaceTransfer(skcms_TFType transferType) {
+    switch (transferType) {
+        case skcms_TFType_sRGBish:
+            return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_SRGBISH;
+        case skcms_TFType_PQish:
+            return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_PQISH;
+        case skcms_TFType_HLGish:
+            return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_HLGISH;
+        default:
+            return stats::IMAGE_DECODED__COLOR_SPACE_TRANSFER__COLOR_SPACE_TRANSFER_UNKNOWN;
+    }
+}
+
+int32_t toStatsBitmapFormat(SkColorType type) {
+    switch (type) {
+        case kAlpha_8_SkColorType:
+            return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_A_8;
+        case kRGB_565_SkColorType:
+            return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_RGB_565;
+        case kN32_SkColorType:
+            return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_ARGB_8888;
+        case kRGBA_F16_SkColorType:
+            return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_RGBA_F16;
+        case kRGBA_1010102_SkColorType:
+            return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_RGBA_1010102;
+        default:
+            return stats::IMAGE_DECODED__FORMAT__BITMAP_FORMAT_UNKNOWN;
+    }
+}
+
+}  // namespace
+
+#endif
+
+void logBitmapDecode(const SkImageInfo& info, bool hasGainmap) {
+#ifdef __ANDROID__
+
+    if (!statssocket::lazy::IsAvailable()) {
+        std::once_flag once;
+        std::call_once(once, []() { ALOGD("libstatssocket not available, dropping stats"); });
+        return;
+    }
+
+    skcms_TFType tfnType = skcms_TFType_Invalid;
+
+    if (info.colorSpace()) {
+        skcms_TransferFunction tfn;
+        info.colorSpace()->transferFn(&tfn);
+        tfnType = skcms_TransferFunction_getType(&tfn);
+    }
+
+    auto status =
+            stats::stats_write(uirenderer::stats::IMAGE_DECODED, static_cast<int32_t>(getuid()),
+                               uirenderer::toStatsColorSpaceTransfer(tfnType), hasGainmap,
+                               uirenderer::toStatsBitmapFormat(info.colorType()));
+    ALOGW_IF(status != OK, "Image decoding logging dropped!");
+#endif
+}
+
+void logBitmapDecode(const Bitmap& bitmap) {
+    logBitmapDecode(bitmap.info(), bitmap.hasGainmap());
+}
+
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/utils/StatsUtils.h b/libs/hwui/utils/StatsUtils.h
new file mode 100644
index 0000000..0c247014
--- /dev/null
+++ b/libs/hwui/utils/StatsUtils.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <SkBitmap.h>
+#include <SkColorSpace.h>
+#include <SkColorType.h>
+#include <cutils/compiler.h>
+#include <hwui/Bitmap.h>
+
+namespace android {
+namespace uirenderer {
+
+ANDROID_API void logBitmapDecode(const SkImageInfo& info, bool hasGainmap);
+
+ANDROID_API void logBitmapDecode(const Bitmap& bitmap);
+
+}  // namespace uirenderer
+}  // namespace android
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/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 1024a55..1db7198 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1202,7 +1202,6 @@
                     break;
                 case AudioSystem.STREAM_BLUETOOTH_SCO:
                     mContentType = CONTENT_TYPE_SPEECH;
-                    mFlags |= FLAG_SCO;
                     break;
                 case AudioSystem.STREAM_DTMF:
                     mContentType = CONTENT_TYPE_SONIFICATION;
@@ -1750,8 +1749,7 @@
                     AudioSystem.STREAM_SYSTEM : AudioSystem.STREAM_SYSTEM_ENFORCED;
         }
         if ((aa.getAllFlags() & FLAG_SCO) == FLAG_SCO) {
-            return fromGetVolumeControlStream ?
-                    AudioSystem.STREAM_VOICE_CALL : AudioSystem.STREAM_BLUETOOTH_SCO;
+            return AudioSystem.STREAM_VOICE_CALL;
         }
         if ((aa.getAllFlags() & FLAG_BEACON) == FLAG_BEACON) {
             return fromGetVolumeControlStream ?
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 39b29d0..2da8eec 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -16,11 +16,15 @@
 
 package android.media;
 
+import static android.media.audio.Flags.FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE;
+
 import android.Manifest;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
+import android.media.audio.Flags;
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -192,6 +196,15 @@
      */
     public static final int TYPE_DOCK_ANALOG = 31;
 
+    /**
+     * A device type describing a speaker group that supports multichannel contents. The speakers in
+     * the group are connected together using local network based protocols. The speaker group
+     * requires additional input of the physical positions of each individual speaker to provide a
+     * better experience on multichannel contents.
+     */
+    @FlaggedApi(FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE)
+    public static final int TYPE_MULTICHANNEL_GROUP = 32;
+
     /** @hide */
     @IntDef(flag = false, prefix = "TYPE", value = {
             TYPE_BUILTIN_EARPIECE,
@@ -224,7 +237,8 @@
             TYPE_BLE_SPEAKER,
             TYPE_ECHO_REFERENCE,
             TYPE_BLE_BROADCAST,
-            TYPE_DOCK_ANALOG}
+            TYPE_DOCK_ANALOG,
+            TYPE_MULTICHANNEL_GROUP}
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioDeviceType {}
@@ -285,7 +299,8 @@
             TYPE_BLE_HEADSET,
             TYPE_BLE_SPEAKER,
             TYPE_BLE_BROADCAST,
-            TYPE_DOCK_ANALOG}
+            TYPE_DOCK_ANALOG,
+            TYPE_MULTICHANNEL_GROUP}
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioDeviceTypeOut {}
@@ -321,7 +336,13 @@
             case TYPE_BLE_BROADCAST:
             case TYPE_DOCK_ANALOG:
                 return true;
+
             default:
+                if (Flags.enableMultichannelGroupDevice()) {
+                    if (type == TYPE_MULTICHANNEL_GROUP) {
+                        return true;
+                    }
+                }
                 return false;
         }
     }
@@ -665,6 +686,10 @@
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_HEADSET, TYPE_BLE_HEADSET);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_SPEAKER, TYPE_BLE_SPEAKER);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BLE_BROADCAST, TYPE_BLE_BROADCAST);
+        if (Flags.enableMultichannelGroupDevice()) {
+            INT_TO_EXT_DEVICE_MAPPING.put(
+                    AudioSystem.DEVICE_OUT_MULTICHANNEL_GROUP, TYPE_MULTICHANNEL_GROUP);
+        }
 
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, TYPE_BUILTIN_MIC);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, TYPE_BLUETOOTH_SCO);
@@ -721,6 +746,10 @@
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_OUT_BLE_HEADSET);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_SPEAKER, AudioSystem.DEVICE_OUT_BLE_SPEAKER);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BLE_BROADCAST, AudioSystem.DEVICE_OUT_BLE_BROADCAST);
+        if (Flags.enableMultichannelGroupDevice()) {
+            EXT_TO_INT_DEVICE_MAPPING.put(
+                    TYPE_MULTICHANNEL_GROUP, AudioSystem.DEVICE_OUT_MULTICHANNEL_GROUP);
+        }
 
         // privileges mapping to input device
         EXT_TO_INT_INPUT_DEVICE_MAPPING = new SparseIntArray();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4627be3..9beeef4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -21,6 +21,7 @@
 import static android.content.Context.DEVICE_ID_DEFAULT;
 import static android.media.audio.Flags.autoPublicVolumeApiHardening;
 import static android.media.audio.Flags.automaticBtDeviceType;
+import static android.media.audio.Flags.FLAG_DEPRECATE_STREAM_BT_SCO;
 import static android.media.audio.Flags.FLAG_FOCUS_EXCLUSIVE_WITH_RECORDING;
 import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
 import static android.media.audio.Flags.FLAG_SUPPORTED_DEVICE_TYPES_API;
@@ -406,8 +407,10 @@
     /** Used to identify the volume of audio streams for notifications */
     public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
     /** @hide Used to identify the volume of audio streams for phone calls when connected
-     *        to bluetooth */
+     *        to bluetooth
+     *  @deprecated use {@link #STREAM_VOICE_CALL} instead */
     @SystemApi
+    @FlaggedApi(FLAG_DEPRECATE_STREAM_BT_SCO)
     public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
     /** @hide Used to identify the volume of audio streams for enforced system sounds
      *        in certain countries (e.g camera in Japan) */
@@ -1161,7 +1164,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();
         }
@@ -3278,7 +3281,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();
         }
@@ -6156,6 +6159,11 @@
      */
     public static final int DEVICE_OUT_BLE_BROADCAST = AudioSystem.DEVICE_OUT_BLE_BROADCAST;
     /** @hide
+     * The audio output device code for a wireless speaker group supporting multichannel content.
+     */
+    public static final int DEVICE_OUT_MULTICHANNEL_GROUP =
+            AudioSystem.DEVICE_OUT_MULTICHANNEL_GROUP;
+    /** @hide
      * This is not used as a returned value from {@link #getDevicesForStream}, but could be
      *  used in the future in a set method to select whatever default device is chosen by the
      *  platform-specific implementation.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index bf09cb0..d0d91ba 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -93,7 +93,8 @@
     /** @hide Used to identify the volume of audio streams for notifications */
     public static final int STREAM_NOTIFICATION = 5;
     /** @hide
-     *  Used to identify the volume of audio streams for phone calls when connected on bluetooth */
+     *  Used to identify the volume of audio streams for phone calls when connected on bluetooth
+     *  @deprecated use {@link #STREAM_VOICE_CALL} instead */
     public static final int STREAM_BLUETOOTH_SCO = 6;
     /** @hide Used to identify the volume of audio streams for enforced system sounds in certain
      * countries (e.g camera in Japan) */
@@ -110,7 +111,7 @@
     public static final int STREAM_ASSISTANT = 11;
     /**
      * @hide
-     * @deprecated Use {@link #numStreamTypes() instead}
+     * @deprecated Use {@link #numStreamTypes()} instead
      */
     public static final int NUM_STREAMS = 5;
 
@@ -1066,6 +1067,8 @@
     /** @hide */
     public static final int DEVICE_OUT_IP = 0x800000;
     /** @hide */
+    public static final int DEVICE_OUT_MULTICHANNEL_GROUP = 0x800001;
+    /** @hide */
     public static final int DEVICE_OUT_BUS = 0x1000000;
     /** @hide */
     public static final int DEVICE_OUT_PROXY = 0x2000000;
@@ -1134,6 +1137,7 @@
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_AUX_LINE);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_SPEAKER_SAFE);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_IP);
+        DEVICE_OUT_ALL_SET.add(DEVICE_OUT_MULTICHANNEL_GROUP);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_BUS);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_PROXY);
         DEVICE_OUT_ALL_SET.add(DEVICE_OUT_USB_HEADSET);
@@ -1422,6 +1426,8 @@
     /** @hide */ public static final String DEVICE_OUT_AUX_LINE_NAME = "aux_line";
     /** @hide */ public static final String DEVICE_OUT_SPEAKER_SAFE_NAME = "speaker_safe";
     /** @hide */ public static final String DEVICE_OUT_IP_NAME = "ip";
+    /** @hide */
+    public static final String DEVICE_OUT_MULTICHANNEL_GROUP_NAME = "multichannel_group";
     /** @hide */ public static final String DEVICE_OUT_BUS_NAME = "bus";
     /** @hide */ public static final String DEVICE_OUT_PROXY_NAME = "proxy";
     /** @hide */ public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset";
@@ -1515,6 +1521,8 @@
             return DEVICE_OUT_SPEAKER_SAFE_NAME;
         case DEVICE_OUT_IP:
             return DEVICE_OUT_IP_NAME;
+        case DEVICE_OUT_MULTICHANNEL_GROUP:
+            return DEVICE_OUT_MULTICHANNEL_GROUP_NAME;
         case DEVICE_OUT_BUS:
             return DEVICE_OUT_BUS_NAME;
         case DEVICE_OUT_PROXY:
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index b022ea1..88efed5 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -16,12 +16,16 @@
 
 package android.media;
 
+import static android.media.tv.flags.Flags.FLAG_SET_RESOURCE_HOLDER_RETAIN;
+
 import static com.android.media.flags.Flags.FLAG_UPDATE_CLIENT_PROFILE_PRIORITY;
 
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.content.Context;
 import android.hardware.cas.AidlCasPluginDescriptor;
@@ -511,18 +515,20 @@
 
     private final TunerResourceManager.ResourcesReclaimListener mResourceListener =
             new TunerResourceManager.ResourcesReclaimListener() {
-            @Override
-            public void onReclaimResources() {
-                synchronized (mSessionMap) {
-                    List<Session> sessionList = new ArrayList<>(mSessionMap.keySet());
-                    for (Session casSession: sessionList) {
-                        casSession.close();
+                @Override
+                public void onReclaimResources() {
+                    synchronized (mSessionMap) {
+                        List<Session> sessionList = new ArrayList<>(mSessionMap.keySet());
+                        for (Session casSession : sessionList) {
+                            casSession.close();
+                        }
+                    }
+                    if (mEventHandler != null) {
+                        mEventHandler.sendMessage(
+                                mEventHandler.obtainMessage(EventHandler.MSG_CAS_RESOURCE_LOST));
                     }
                 }
-                mEventHandler.sendMessage(mEventHandler.obtainMessage(
-                        EventHandler.MSG_CAS_RESOURCE_LOST));
-            }
-        };
+            };
 
     /**
      * Describe a CAS plugin with its CA_system_ID and string name.
@@ -988,12 +994,32 @@
      * @param priority the new priority. Any negative value would cause no-op on priority setting
      *     and the API would only process nice value setting in that case.
      * @param niceValue the nice value.
+     * @hide
      */
     @FlaggedApi(FLAG_UPDATE_CLIENT_PROFILE_PRIORITY)
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
     public boolean updateResourcePriority(int priority, int niceValue) {
         return mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue);
     }
 
+    /**
+     * Determines whether the resource holder retains ownership of the resource during a challenge
+     * scenario, when both resource holder and resource challenger have same processId and same
+     * priority.
+     *
+     * @param resourceHolderRetain Set to {@code true} to allow the resource holder to retain
+     *     ownership, or false to allow the resource challenger to acquire the resource.
+     *     If not explicitly set, resourceHolderRetain is set to {@code false}.
+     * @hide
+     */
+    @FlaggedApi(FLAG_SET_RESOURCE_HOLDER_RETAIN)
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+    public void setResourceHolderRetain(boolean resourceHolderRetain) {
+        mTunerResourceManager.setResourceHolderRetain(mClientId, resourceHolderRetain);
+    }
+
     IHwBinder getBinder() {
         if (mICas != null) {
             return null; // Return IHwBinder only for HIDL
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 3a19f46..96edd63 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -23,6 +23,7 @@
 import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
 import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
 import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
+import static android.media.codec.Flags.FLAG_APV_SUPPORT;
 import static android.media.MediaCodec.GetFlag;
 
 import android.annotation.FlaggedApi;
@@ -4496,6 +4497,265 @@
         @SuppressLint("AllUpper")
         public static final int AC4Level4       = 0x10;
 
+        // Profiles and levels/bands for APV Codec, corresponding to the definitions in
+        // "Advanced Professional Video", 10.1.3 Profiles, 10.1.4 Levels and Bands
+        // found at https://www.ietf.org/archive/id/draft-lim-apv-02.html
+
+        /**
+         * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1
+         */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVProfile422_10 =  0x01;
+
+        /**
+         * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1
+         * with HDR10.
+         */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVProfile422_10HDR10 =  0x1000;
+
+        /**
+         * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1
+         * with HDR10Plus.
+         */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVProfile422_10HDR10Plus =  0x2000;
+
+        // For APV Levels, the numerical values are constructed as follows:
+        //   ((0x100 << (level_num - 1)) | (1 << band))
+        // where:
+        //   - "level_num" is the APV Level numbered consecutively
+        //     (i.e., Level 1 == 1, Level 1.1 == 2, etc.)
+        //   - "band" is the APV Band
+
+        /** APV Codec Level 1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel1Band0 =  0x101;
+        /** APV Codec Level 1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel1Band1 =  0x102;
+        /** APV Codec Level 1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel1Band2 =  0x104;
+        /** APV Codec Level 1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel1Band3 =  0x108;
+        /** APV Codec Level 1.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel11Band0 = 0x201;
+        /** APV Codec Level 1.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel11Band1 = 0x202;
+        /** APV Codec Level 1.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel11Band2 = 0x204;
+        /** APV Codec Level 1.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel11Band3 = 0x208;
+        /** APV Codec Level 2, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel2Band0 =  0x401;
+        /** APV Codec Level 2, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel2Band1 =  0x402;
+        /** APV Codec Level 2, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel2Band2 =  0x404;
+        /** APV Codec Level 2, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel2Band3 =  0x408;
+        /** APV Codec Level 2.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel21Band0 = 0x801;
+        /** APV Codec Level 2.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel21Band1 = 0x802;
+        /** APV Codec Level 2.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel21Band2 = 0x804;
+        /** APV Codec Level 2.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel21Band3 = 0x808;
+        /** APV Codec Level 3, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel3Band0 =  0x1001;
+        /** APV Codec Level 3, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel3Band1 =  0x1002;
+        /** APV Codec Level 3, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel3Band2 =  0x1004;
+        /** APV Codec Level 3, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel3Band3 =  0x1008;
+        /** APV Codec Level 3.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel31Band0 = 0x2001;
+        /** APV Codec Level 3.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel31Band1 = 0x2002;
+        /** APV Codec Level 3.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel31Band2 = 0x2004;
+        /** APV Codec Level 3.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel31Band3 = 0x2008;
+        /** APV Codec Level 4, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel4Band0 =  0x4001;
+        /** APV Codec Level 4, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel4Band1 =  0x4002;
+        /** APV Codec Level 4, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel4Band2 =  0x4004;
+        /** APV Codec Level 4, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel4Band3 =  0x4008;
+        /** APV Codec Level 4.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel41Band0 = 0x8001;
+        /** APV Codec Level 4.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel41Band1 = 0x8002;
+        /** APV Codec Level 4.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel41Band2 = 0x8004;
+        /** APV Codec Level 4.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel41Band3 = 0x8008;
+        /** APV Codec Level 5, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel5Band0 =  0x10001;
+        /** APV Codec Level 5, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel5Band1 =  0x10002;
+        /** APV Codec Level 5, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel5Band2 =  0x10004;
+        /** APV Codec Level 5, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel5Band3 =  0x10008;
+        /** APV Codec Level 5.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel51Band0 = 0x20001;
+        /** APV Codec Level 5.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel51Band1 = 0x20002;
+        /** APV Codec Level 5.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel51Band2 = 0x20004;
+        /** APV Codec Level 5.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel51Band3 = 0x20008;
+        /** APV Codec Level 6, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel6Band0 =  0x40001;
+        /** APV Codec Level 6, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel6Band1 =  0x40002;
+        /** APV Codec Level 6, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel6Band2 =  0x40004;
+        /** APV Codec Level 6, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel6Band3 =  0x40008;
+        /** APV Codec Level 6.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel61Band0 = 0x80001;
+        /** APV Codec Level 6.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel61Band1 = 0x80002;
+        /** APV Codec Level 6.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel61Band2 = 0x80004;
+        /** APV Codec Level 6.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel61Band3 = 0x80008;
+        /** APV Codec Level 7, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel7Band0 =  0x100001;
+        /** APV Codec Level 7, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel7Band1 =  0x100002;
+        /** APV Codec Level 7, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel7Band2 =  0x100004;
+        /** APV Codec Level 7, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel7Band3 =  0x100008;
+        /** APV Codec Level 7.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel71Band0 = 0x200001;
+        /** APV Codec Level 7.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel71Band1 = 0x200002;
+        /** APV Codec Level 7.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel71Band2 = 0x200004;
+        /** APV Codec Level 7.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+        @SuppressLint("AllUpper")
+        @FlaggedApi(FLAG_APV_SUPPORT)
+        public static final int APVLevel71Band3 = 0x200008;
+
         /**
          * The profile of the media content. Depending on the type of media this can be
          * one of the profile values defined in this class.
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index cd0654c..b08a86e 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -18,6 +18,7 @@
 
 import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
 import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
+import static android.media.codec.Flags.FLAG_APV_SUPPORT;
 
 import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE;
 import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
@@ -157,6 +158,8 @@
 public final class MediaFormat {
     public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
     public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+    @FlaggedApi(FLAG_APV_SUPPORT)
+    public static final String MIMETYPE_VIDEO_APV = "video/apv";
     public static final String MIMETYPE_VIDEO_AV1 = "video/av01";
     public static final String MIMETYPE_VIDEO_AVC = "video/avc";
     public static final String MIMETYPE_VIDEO_HEVC = "video/hevc";
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/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index bdfa6301..2d17bf5 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -387,13 +387,13 @@
         /**
          * Audio source for capturing broadcast radio tuner output.
          * Capturing the radio tuner output requires the
-         * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission.
+         * {@link android.Manifest.permission#CAPTURE_TUNER_AUDIO_INPUT} permission.
          * This permission is reserved for use by system components and is not available to
          * third-party applications.
          * @hide
          */
         @SystemApi
-        @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)
+        @RequiresPermission(android.Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT)
         public static final int RADIO_TUNER = 1998;
 
         /**
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index e048d5c..816729d 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import static android.media.MediaRouter2Utils.toUniqueId;
+import static android.media.audio.Flags.FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE;
 
 import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER;
 import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
@@ -272,6 +273,19 @@
     public static final int TYPE_BLE_HEADSET = AudioDeviceInfo.TYPE_BLE_HEADSET;
 
     /**
+     * Indicates the route is a speaker group supporting multichannel contents.
+     *
+     * <p>The speakers in the group are connected together using local network based protocols. The
+     * speaker group requires additional input of the physical positions of each individual speaker
+     * to provide a better experience on multichannel contents.
+     *
+     * @see #getType
+     */
+    @FlaggedApi(FLAG_ENABLE_MULTICHANNEL_GROUP_DEVICE)
+    public static final int TYPE_MULTICHANNEL_SPEAKER_GROUP =
+            AudioDeviceInfo.TYPE_MULTICHANNEL_GROUP;
+
+    /**
      * Indicates the route is a remote TV.
      *
      * <p>A remote device uses a routing protocol managed by the application, as opposed to the
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/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
new file mode 100644
index 0000000..e413a50
--- /dev/null
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -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 android.media.quality;
+
+import android.media.quality.PictureProfile;
+
+/**
+ * Interface for Media Quality Manager
+ * @hide
+ */
+interface IMediaQualityManager {
+    PictureProfile createPictureProfile(in PictureProfile pp);
+    PictureProfile getPictureProfileById(in long id);
+    List<PictureProfile> getPictureProfilesByPackage(in String packageName);
+    List<PictureProfile> getAvailablePictureProfiles();
+    List<PictureProfile> getAvailableAllPictureProfiles();
+}
\ No newline at end of file
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/media/java/android/media/quality/IPictureProfileCallback.aidl
similarity index 65%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to media/java/android/media/quality/IPictureProfileCallback.aidl
index 83cbc52..5deb029 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/media/java/android/media/quality/IPictureProfileCallback.aidl
@@ -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 android.media.quality;
+
+import android.media.quality.PictureProfile;
+
+/**
+ * Interface to receive callbacks from IMediaQuality.
+ * @hide
+ */
+oneway interface IPictureProfileCallback {
+    void onPictureProfileAdded(in long id, in PictureProfile p);
+    void onPictureProfileUpdated(in long id, in PictureProfile p);
 }
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
new file mode 100644
index 0000000..5cbc81d
--- /dev/null
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.quality;
+
+
+import android.annotation.FlaggedApi;
+import android.media.tv.flags.Flags;
+
+/**
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public class MediaQualityContract {
+
+    public interface BaseParameters {
+        String PARAMETER_ID = "_id";
+        String PARAMETER_NAME = "_name";
+        String PARAMETER_PACKAGE = "_package";
+        String PARAMETER_INPUT_ID = "_input_id";
+
+    }
+    public static final class PictureQuality implements BaseParameters {
+        public static final String PARAMETER_BRIGHTNESS = "brightness";
+        public static final String PARAMETER_CONTRAST = "contrast";
+        public static final String PARAMETER_SHARPNESS = "sharpness";
+        public static final String PARAMETER_SATURATION = "saturation";
+    }
+
+    public static final class SoundQuality implements BaseParameters {
+        public static final String PARAMETER_BALANCE = "balance";
+        public static final String PARAMETER_BASS = "bass";
+        public static final String PARAMETER_TREBLE = "treble";
+    }
+
+    private MediaQualityContract() {
+    }
+}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
new file mode 100644
index 0000000..03dc24d
--- /dev/null
+++ b/media/java/android/media/quality/MediaQualityManager.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 android.media.quality;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.media.tv.flags.Flags;
+import android.os.RemoteException;
+
+import androidx.annotation.RequiresPermission;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * 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;
+    private final Object mLock = new Object();
+    // @GuardedBy("mLock")
+    private final List<PictureProfileCallbackRecord> mPpCallbackRecords = new ArrayList<>();
+
+    /**
+     * @hide
+     */
+    public MediaQualityManager(Context context, IMediaQualityManager service) {
+        mContext = context;
+        mService = service;
+        IPictureProfileCallback mqCallback = new IPictureProfileCallback.Stub() {
+            @Override
+            public void onPictureProfileAdded(long profileId, PictureProfile profile) {
+                synchronized (mLock) {
+                    for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
+                        // TODO: filter callback record
+                        record.postPictureProfileAdded(profileId, profile);
+                    }
+                }
+            }
+            @Override
+            public void onPictureProfileUpdated(long profileId, PictureProfile profile) {
+                synchronized (mLock) {
+                    for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
+                        // TODO: filter callback record
+                        record.postPictureProfileUpdated(profileId, profile);
+                    }
+                }
+            }
+        };
+    }
+
+    /**
+     * Registers a {@link PictureProfileCallback}.
+     * @hide
+     */
+    public void registerCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull PictureProfileCallback callback) {
+        Preconditions.checkNotNull(callback);
+        Preconditions.checkNotNull(executor);
+        synchronized (mLock) {
+            mPpCallbackRecords.add(new PictureProfileCallbackRecord(callback, executor));
+        }
+    }
+
+    /**
+     * Unregisters the existing {@link PictureProfileCallback}.
+     * @hide
+     */
+    public void unregisterCallback(@NonNull final PictureProfileCallback callback) {
+        Preconditions.checkNotNull(callback);
+        synchronized (mLock) {
+            for (Iterator<PictureProfileCallbackRecord> it = mPpCallbackRecords.iterator();
+                    it.hasNext(); ) {
+                PictureProfileCallbackRecord record = it.next();
+                if (record.getCallback() == callback) {
+                    it.remove();
+                    break;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Gets picture profile by given profile ID.
+     * @return the corresponding picture profile if available; {@code null} if the ID doesn't
+     *         exist or the profile is not accessible to the caller.
+     */
+    public PictureProfile getPictureProfileById(long profileId) {
+        try {
+            return mService.getPictureProfileById(profileId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /** @SystemApi gets profiles that available to the given package */
+    @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+    public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
+        try {
+            return mService.getPictureProfilesByPackage(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Gets profiles that available to the caller package */
+    public List<PictureProfile> getAvailablePictureProfiles() {
+        try {
+            return mService.getAvailablePictureProfiles();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @SystemApi all stored picture profiles */
+    @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+    public List<PictureProfile> getAvailableAllPictureProfiles() {
+        try {
+            return mService.getAvailableAllPictureProfiles();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Creates a picture profile and store it in the system.
+     *
+     * @return the stored profile with an assigned profile ID.
+     */
+    public PictureProfile createPictureProfile(PictureProfile pp) {
+        try {
+            return mService.createPictureProfile(pp);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private static final class PictureProfileCallbackRecord {
+        private final PictureProfileCallback mCallback;
+        private final Executor mExecutor;
+
+        PictureProfileCallbackRecord(PictureProfileCallback callback, Executor executor) {
+            mCallback = callback;
+            mExecutor = executor;
+        }
+
+        public PictureProfileCallback getCallback() {
+            return mCallback;
+        }
+
+        public void postPictureProfileAdded(final long id, PictureProfile profile) {
+
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onPictureProfileAdded(id, profile);
+                }
+            });
+        }
+
+        public void postPictureProfileUpdated(final long id, PictureProfile profile) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onPictureProfileUpdated(id, profile);
+                }
+            });
+        }
+    }
+
+    /**
+     * Callback used to monitor status of picture profiles.
+     * @hide
+     */
+    public abstract static class PictureProfileCallback {
+        /**
+         * @hide
+         */
+        public void onPictureProfileAdded(long id, PictureProfile profile) {
+        }
+        /**
+         * @hide
+         */
+        public void onPictureProfileUpdated(long id, PictureProfile profile) {
+        }
+    }
+}
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/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/media/java/android/media/quality/PictureProfile.aidl
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to media/java/android/media/quality/PictureProfile.aidl
index 83cbc52..41d018b 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/media/java/android/media/quality/PictureProfile.aidl
@@ -13,10 +13,7 @@
  * 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;
+
+parcelable PictureProfile;
diff --git a/media/java/android/media/quality/PictureProfile.java b/media/java/android/media/quality/PictureProfile.java
new file mode 100644
index 0000000..36c49d8
--- /dev/null
+++ b/media/java/android/media/quality/PictureProfile.java
@@ -0,0 +1,223 @@
+/*
+ * 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.media.tv.flags.Flags;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * @hide
+ */
+
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public class PictureProfile implements Parcelable {
+    @Nullable
+    private Long mId;
+    @NonNull
+    private final String mName;
+    @Nullable
+    private final String mInputId;
+    @Nullable
+    private final String mPackageName;
+    @NonNull
+    private final Bundle mParams;
+
+    protected PictureProfile(Parcel in) {
+        if (in.readByte() == 0) {
+            mId = null;
+        } else {
+            mId = in.readLong();
+        }
+        mName = in.readString();
+        mInputId = in.readString();
+        mPackageName = in.readString();
+        mParams = in.readBundle();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mId == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeLong(mId);
+        }
+        dest.writeString(mName);
+        dest.writeString(mInputId);
+        dest.writeString(mPackageName);
+        dest.writeBundle(mParams);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<PictureProfile> CREATOR = new Creator<PictureProfile>() {
+        @Override
+        public PictureProfile createFromParcel(Parcel in) {
+            return new PictureProfile(in);
+        }
+
+        @Override
+        public PictureProfile[] newArray(int size) {
+            return new PictureProfile[size];
+        }
+    };
+
+
+    /**
+     * Creates a new PictureProfile.
+     *
+     * @hide
+     */
+    public PictureProfile(
+            @Nullable Long id,
+            @NonNull String name,
+            @Nullable String inputId,
+            @Nullable String packageName,
+            @NonNull Bundle params) {
+        this.mId = id;
+        this.mName = name;
+        com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
+        this.mInputId = inputId;
+        this.mPackageName = packageName;
+        this.mParams = params;
+    }
+
+    @Nullable
+    public Long getProfileId() {
+        return mId;
+    }
+
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    @Nullable
+    public String getInputId() {
+        return mInputId;
+    }
+
+    @Nullable
+    public String getPackageName() {
+        return mPackageName;
+    }
+    @NonNull
+    public Bundle getParameters() {
+        return new Bundle(mParams);
+    }
+
+    /**
+     * A builder for {@link PictureProfile}
+     */
+    public static class Builder {
+        @Nullable
+        private Long mId;
+        @NonNull
+        private String mName;
+        @Nullable
+        private String mInputId;
+        @Nullable
+        private String mPackageName;
+        @NonNull
+        private Bundle mParams;
+
+        /**
+         * Creates a new Builder.
+         *
+         * @hide
+         */
+        public Builder(@NonNull String name) {
+            mName = name;
+            com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
+        }
+
+        /**
+         * Copy constructor.
+         *
+         * @hide
+         */
+        public Builder(@NonNull PictureProfile p) {
+            mId = null; // ID needs to be reset
+            mName = p.getName();
+            mPackageName = p.getPackageName();
+            mInputId = p.getInputId();
+        }
+
+        /* @hide using by MediaQualityService */
+
+        /**
+         * Sets profile ID.
+         * @hide using by MediaQualityService
+         */
+        @NonNull
+        public Builder setProfileId(@Nullable Long id) {
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Sets input ID.
+         */
+        @NonNull
+        public Builder setInputId(@NonNull String value) {
+            mInputId = value;
+            return this;
+        }
+
+        /**
+         * Sets package name of the profile.
+         */
+        @NonNull
+        public Builder setPackageName(@NonNull String value) {
+            mPackageName = value;
+            return this;
+        }
+
+        /**
+         * Sets profile parameters.
+         */
+        @NonNull
+        public Builder setParameters(@NonNull Bundle params) {
+            mParams = new Bundle(params);
+            return this;
+        }
+
+        /**
+         * Builds the instance.
+         */
+        @NonNull
+        public PictureProfile build() {
+
+            PictureProfile o = new PictureProfile(
+                    mId,
+                    mName,
+                    mInputId,
+                    mPackageName,
+                    mParams);
+            return o;
+        }
+    }
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/media/java/android/media/quality/SoundProfile.aidl
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to media/java/android/media/quality/SoundProfile.aidl
index 83cbc52..e79fcaa 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/media/java/android/media/quality/SoundProfile.aidl
@@ -13,10 +13,7 @@
  * 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;
+
+parcelable SoundProfile;
diff --git a/media/java/android/media/quality/SoundProfile.java b/media/java/android/media/quality/SoundProfile.java
new file mode 100644
index 0000000..e0fcf9d
--- /dev/null
+++ b/media/java/android/media/quality/SoundProfile.java
@@ -0,0 +1,197 @@
+/*
+ * 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.media.tv.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
+public class SoundProfile implements Parcelable {
+    @Nullable
+    private Long mId;
+    @NonNull
+    private final String mName;
+    @Nullable
+    private final String mInputId;
+    @Nullable
+    private final String mPackageName;
+
+    protected SoundProfile(Parcel in) {
+        if (in.readByte() == 0) {
+            mId = null;
+        } else {
+            mId = in.readLong();
+        }
+        mName = in.readString();
+        mInputId = in.readString();
+        mPackageName = in.readString();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mId == null) {
+            dest.writeByte((byte) 0);
+        } else {
+            dest.writeByte((byte) 1);
+            dest.writeLong(mId);
+        }
+        dest.writeString(mName);
+        dest.writeString(mInputId);
+        dest.writeString(mPackageName);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<SoundProfile> CREATOR = new Creator<SoundProfile>() {
+        @Override
+        public SoundProfile createFromParcel(Parcel in) {
+            return new SoundProfile(in);
+        }
+
+        @Override
+        public SoundProfile[] newArray(int size) {
+            return new SoundProfile[size];
+        }
+    };
+
+
+    /**
+     * Creates a new SoundProfile.
+     *
+     * @hide
+     */
+    public SoundProfile(
+            @Nullable Long id,
+            @NonNull String name,
+            @Nullable String inputId,
+            @Nullable String packageName) {
+        this.mId = id;
+        this.mName = name;
+        com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
+        this.mInputId = inputId;
+        this.mPackageName = packageName;
+    }
+
+    @Nullable
+    public Long getProfileId() {
+        return mId;
+    }
+
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    @Nullable
+    public String getInputId() {
+        return mInputId;
+    }
+
+    @Nullable
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * A builder for {@link SoundProfile}
+     */
+    public static class Builder {
+        @Nullable
+        private Long mId;
+        @NonNull
+        private String mName;
+        @Nullable
+        private String mInputId;
+        @Nullable
+        private String mPackageName;
+
+        /**
+         * Creates a new Builder.
+         *
+         * @hide
+         */
+        public Builder(@NonNull String name) {
+            mName = name;
+            com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
+        }
+
+        /**
+         * Copy constructor.
+         *
+         * @hide
+         */
+        public Builder(@NonNull SoundProfile p) {
+            mId = null; // ID needs to be reset
+            mName = p.getName();
+            mPackageName = p.getPackageName();
+            mInputId = p.getInputId();
+        }
+
+        /**
+         * Sets profile ID.
+         * @hide using by MediaQualityService
+         */
+        @NonNull
+        public Builder setProfileId(@Nullable Long id) {
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Sets input ID.
+         */
+        @NonNull
+        public Builder setInputId(@NonNull String value) {
+            mInputId = value;
+            return this;
+        }
+
+        /**
+         * Sets package name of the profile.
+         */
+        @NonNull
+        public Builder setPackageName(@NonNull String value) {
+            mPackageName = value;
+            return this;
+        }
+
+        /**
+         * Builds the instance.
+         */
+        @NonNull
+        public SoundProfile build() {
+
+            SoundProfile o = new SoundProfile(
+                    mId,
+                    mName,
+                    mInputId,
+                    mPackageName);
+            return o;
+        }
+    }
+}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index b673e03..c2f6896 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -27,6 +27,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.annotation.UserIdInt;
 import android.content.AttributionSource;
 import android.content.Context;
 import android.content.Intent;
@@ -760,6 +761,7 @@
      * @hide
      */
     public static final int UNKNOWN_CLIENT_PID = -1;
+
     /**
      * An unknown state of the client userId gets from the TvInputManager. Client gets this value
      * when query through {@link #getClientUserId(String sessionId)} fails.
@@ -2526,9 +2528,10 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+    @SystemApi
     @FlaggedApi(Flags.FLAG_KIDS_MODE_TVDB_SHARING)
-    public int getClientUserId(@NonNull String sessionId) {
+    @RequiresPermission(android.Manifest.permission.SINGLE_USER_TIS_ACCESS)
+    public @UserIdInt int getClientUserId(@NonNull String sessionId) {
         return getClientUserIdInternal(sessionId);
     }
 
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index d49f7dd..4de6863 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -71,4 +71,12 @@
     namespace: "media_tv"
     description: "Standardize AIDL Extension Interface of TIS"
     bug: "330366987"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "set_resource_holder_retain"
+    is_exported: true
+    namespace: "media_tv"
+    description : "Feature flag to add setResourceHolderRetain api to MediaCas and Tuner JAVA."
+    bug: "372973197"
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index cdf50ec..b1adb77 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -16,6 +16,8 @@
 
 package android.media.tv.tuner;
 
+import static android.media.tv.flags.Flags.FLAG_SET_RESOURCE_HOLDER_RETAIN;
+
 import android.annotation.BytesLong;
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
@@ -751,6 +753,21 @@
     }
 
     /**
+     * Determines whether the resource holder retains ownership of the resource during a challenge
+     * scenario, when both resource holder and resource challenger have same processId and same
+     * priority.
+     *
+     * @param resourceHolderRetain Set to true to allow the resource holder to retain ownership, or
+     *     false to allow the resource challenger to acquire the resource. If not explicitly set,
+     *     resourceHolderRetain is set to false.
+     */
+    @FlaggedApi(FLAG_SET_RESOURCE_HOLDER_RETAIN)
+    @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+    public void setResourceHolderRetain(boolean resourceHolderRetain) {
+        mTunerResourceManager.setResourceHolderRetain(mClientId, resourceHolderRetain);
+    }
+
+    /**
      * Checks if there is an unused frontend resource available.
      *
      * @param frontendType {@link android.media.tv.tuner.frontend.FrontendSettings.Type} for the
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index bb581eb..be65ad9 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -40,8 +40,11 @@
  * <p>Resources include:
  * <ul>
  * <li>TunerFrontend {@link android.media.tv.tuner.frontend}.
+ * <li>Demux {@link com.android.server.tv.tunerresourcemanager.DemuxResource}.
+ * <li>Descrambler {@link android.media.tv.tuner.Descrambler}.
  * <li>TunerLnb {@link android.media.tv.tuner.Lnb}.
  * <li>MediaCas {@link android.media.MediaCas}.
+ * <li>CiCam {@link com.android.server.tv.tunerresourcemanager.CiCamResource}.
  * <ul>
  *
  * <p>Expected workflow is:
@@ -78,7 +81,7 @@
         TUNER_RESOURCE_TYPE_LNB,
         TUNER_RESOURCE_TYPE_CAS_SESSION,
         TUNER_RESOURCE_TYPE_FRONTEND_CICAM,
-        TUNER_RESOURCE_TYPE_MAX,
+        TUNER_RESOURCE_TYPE_MAX, // upper bound of constants
      })
     @Retention(RetentionPolicy.SOURCE)
     public @interface TunerResourceType {}
@@ -220,6 +223,25 @@
     }
 
     /**
+     * Determines whether the resource holder retains ownership of the resource during a challenge
+     * scenario, when both resource holder and resource challenger have same processId and same
+     * priority.
+     *
+     * @param clientId The client id used to set ownership of resource to owner in case of resource
+     *     challenger situation.
+     * @param resourceHolderRetain Set to true to allow the resource holder to retain ownership, or
+     *     false to allow the resource challenger to acquire the resource. If not explicitly set,
+     *     resourceHolderRetain is set to false.
+     */
+    public void setResourceHolderRetain(int clientId, boolean resourceHolderRetain) {
+        try {
+            mService.setResourceHolderRetain(clientId, resourceHolderRetain);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Stores the frontend resource map if it was stored before.
      *
      * <p>This API is only for testing purpose and should be used in pair with
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 109c791..c57be1b0 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -151,6 +151,18 @@
      */
     void setLnbInfoList(in long[] lnbIds);
 
+    /**
+     * Determines whether the Resource Holder retains ownership of the resource during a challenge
+     * scenario, when both Resource Holder and Resource Challenger have same processId and same
+     * priority.
+     *
+     * @param clientId The resourceHolderRetain of the client is updated using client ID.
+     * @param resourceHolderRetain set to true to allow the Resource Holder to retain ownership, or
+     *     false to allow the Resource Challenger to acquire the resource. If not explicitly set,
+     *     resourceHolderRetain is set to false.
+     */
+    void setResourceHolderRetain(int clientId, boolean resourceHolderRetain);
+
     /*
      * This API is used by the Tuner framework to request a frontend from the TunerHAL.
      *
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/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 202535d..b025cb8 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -283,6 +283,7 @@
     ASurfaceTransaction_setEnableBackPressure; # introduced=31
     ASurfaceTransaction_setFrameRate; # introduced=30
     ASurfaceTransaction_setFrameRateWithChangeStrategy; # introduced=31
+    ASurfaceTransaction_setFrameRateParams; # introduced=36
     ASurfaceTransaction_clearFrameRate; # introduced=34
     ASurfaceTransaction_setFrameTimeline; # introduced=Tiramisu
     ASurfaceTransaction_setGeometry; # introduced=29
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index e46db6b..698bc84 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -731,6 +731,28 @@
     transaction->setFrameRate(surfaceControl, frameRate, compatibility, changeFrameRateStrategy);
 }
 
+void ASurfaceTransaction_setFrameRateParams(
+        ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+        float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
+        ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+
+    if (desiredMaxRate < desiredMinRate) {
+        ALOGW("desiredMaxRate must be greater than or equal to desiredMinRate");
+        return;
+    }
+    // TODO(b/362798998): Fix plumbing to send modern params
+    int compatibility = fixedSourceRate == 0 ? ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT
+                                             : ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+    double frameRate = compatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+            ? fixedSourceRate
+            : desiredMinRate;
+    transaction->setFrameRate(surfaceControl, frameRate, compatibility, changeFrameRateStrategy);
+}
+
 void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* aSurfaceTransaction,
                                         ASurfaceControl* aSurfaceControl) {
     CHECK_NOT_NULL(aSurfaceTransaction);
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 0fb3049..23dd9b7 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -48,7 +48,9 @@
         "libhwui_internal_headers",
     ],
 
-    static_libs: ["libarect"],
+    static_libs: [
+        "libarect",
+    ],
 
     host_supported: true,
     target: {
@@ -60,6 +62,11 @@
             shared_libs: [
                 "libandroid",
             ],
+            static_libs: [
+                "libstatslog_hwui",
+                "libstatspull_lazy",
+                "libstatssocket_lazy",
+            ],
             version_script: "libjnigraphics.map.txt",
         },
         host: {
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index e18b4a9..cb95bcf 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -14,18 +14,9 @@
  * limitations under the License.
  */
 
-#include "aassetstreamadaptor.h"
-
-#include <android/asset_manager.h>
-#include <android/bitmap.h>
-#include <android/data_space.h>
-#include <android/imagedecoder.h>
 #include <MimeType.h>
-#include <android/rect.h>
-#include <hwui/ImageDecoder.h>
-#include <log/log.h>
-#include <SkAndroidCodec.h>
 #include <SkAlphaType.h>
+#include <SkAndroidCodec.h>
 #include <SkCodec.h>
 #include <SkCodecAnimation.h>
 #include <SkColorSpace.h>
@@ -35,14 +26,24 @@
 #include <SkRefCnt.h>
 #include <SkSize.h>
 #include <SkStream.h>
-#include <utils/Color.h>
-
+#include <android/asset_manager.h>
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <android/imagedecoder.h>
+#include <android/rect.h>
 #include <fcntl.h>
-#include <limits>
-#include <optional>
+#include <hwui/ImageDecoder.h>
+#include <log/log.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <utils/Color.h>
+#include <utils/StatsUtils.h>
+
+#include <limits>
+#include <optional>
+
+#include "aassetstreamadaptor.h"
 
 using namespace android;
 
@@ -400,9 +401,7 @@
     return info.minRowBytes();
 }
 
-int AImageDecoder_decodeImage(AImageDecoder* decoder,
-                              void* pixels, size_t stride,
-                              size_t size) {
+int AImageDecoder_decodeImage(AImageDecoder* decoder, void* pixels, size_t stride, size_t size) {
     if (!decoder || !pixels || !stride) {
         return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
     }
@@ -419,7 +418,13 @@
         return ANDROID_IMAGE_DECODER_FINISHED;
     }
 
-    return ResultToErrorCode(imageDecoder->decode(pixels, stride));
+    const auto result = ResultToErrorCode(imageDecoder->decode(pixels, stride));
+
+    if (result == ANDROID_IMAGE_DECODER_SUCCESS) {
+        uirenderer::logBitmapDecode(imageDecoder->getOutputInfo(), false);
+    }
+
+    return result;
 }
 
 void AImageDecoder_delete(AImageDecoder* decoder) {
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 520ba89..474ff8c 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -82,6 +82,13 @@
     private boolean mRfDiscoveryStarted = false;
 
     /**
+     * Broadcast Action: Sent on NFC stack initialization when NFC OEM extensions are enabled.
+     * <p> OEM extension modules should use this intent to start their extension service </p>
+     * @hide
+     */
+    public static final String ACTION_OEM_EXTENSION_INIT = "android.nfc.action.OEM_EXTENSION_INIT";
+
+    /**
      * Mode Type for {@link #setControllerAlwaysOnMode(int)}.
      * Enables the controller in default mode when NFC is disabled (existing API behavior).
      * works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}.
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 6a7e693..34f0200 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -165,3 +165,11 @@
     description: "Enabling security log for nfc state change"
     bug: "319934052"
 }
+
+flag {
+    name: "nfc_associated_role_services"
+    is_exported: true
+    namespace: "nfc"
+    description: "Share wallet role routing priority with associated services"
+    bug: "366243361"
+}
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..99d3c8d
--- /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 achieve 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/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index 9cb0ebb..843d2aa 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -114,6 +114,10 @@
     fun notifyChange(key: K, reason: Int)
 }
 
+/** Delegation of [KeyedObservable]. */
+open class KeyedObservableDelegate<K>(delegate: KeyedObservable<K>) :
+    KeyedObservable<K> by delegate
+
 /** A thread safe implementation of [KeyedObservable]. */
 open class KeyedDataObservable<K> : KeyedObservable<K> {
     // Instead of @GuardedBy("this"), guarded by itself because KeyedDataObservable object could be
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/graph.proto b/packages/SettingsLib/Graph/graph.proto
index e93d756..cbe602e 100644
--- a/packages/SettingsLib/Graph/graph.proto
+++ b/packages/SettingsLib/Graph/graph.proto
@@ -74,6 +74,9 @@
   optional ActionTarget action_target = 12;
   // Preference value (if present, it means `persistent` is true).
   optional PreferenceValueProto value = 13;
+  // Intent to show and locate the preference (might have highlight animation on
+  // the preference).
+  optional IntentProto launch_intent = 14;
 
   // Target of an Intent
   message ActionTarget {
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 5e78a29..9cb872a 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -51,9 +51,9 @@
 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"
 
@@ -140,7 +140,7 @@
         addPreferenceScreen(metadata.key) {
             preferenceScreenProto {
                 completeHierarchy = metadata.hasCompleteHierarchy()
-                root = metadata.getPreferenceHierarchy(context).toProto(true)
+                root = metadata.getPreferenceHierarchy(context).toProto(metadata, true)
             }
         }
 
@@ -237,23 +237,29 @@
         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 PreferenceHierarchy.toProto(
+        screenMetadata: PreferenceScreenMetadata,
+        isRoot: Boolean,
+    ): PreferenceGroupProto = preferenceGroupProto {
+        preference = toProto(screenMetadata, this@toProto, isRoot)
+        forEachAsync {
+            addPreferences(
+                preferenceOrGroupProto {
+                    if (it is PreferenceHierarchy) {
+                        group = it.toProto(screenMetadata, false)
+                    } else {
+                        preference = toProto(screenMetadata, it, false)
                     }
-                )
-            }
+                }
+            )
         }
+    }
 
-    private suspend fun toProto(node: PreferenceHierarchyNode, isRoot: Boolean) = preferenceProto {
+    private suspend fun toProto(
+        screenMetadata: PreferenceScreenMetadata,
+        node: PreferenceHierarchyNode,
+        isRoot: Boolean,
+    ) = preferenceProto {
         val metadata = node.metadata
         key = metadata.key
         metadata.getTitleTextProto(isRoot)?.let { title = it }
@@ -291,6 +297,7 @@
             @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata)
         }
         metadata.intent(context)?.let { actionTarget = it.toActionTarget() }
+        screenMetadata.getLaunchIntent(context, metadata)?.let { launchIntent = it.toProto() }
     }
 
     private fun PreferenceMetadata.getTitleTextProto(isRoot: Boolean): TextProto? {
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..386b6d9 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.
      *
@@ -165,8 +162,8 @@
     /**
      * Returns the preference icon.
      *
-     * Implement [PreferenceIconProvider] interface if icon content is provided dynamically
-     * (e.g. icon is provided based on flag value).
+     * Implement [PreferenceIconProvider] interface if icon is provided dynamically (e.g. icon is
+     * provided based on flag value).
      */
     fun getPreferenceIcon(context: Context): Int =
         when {
@@ -215,4 +212,11 @@
      * conditions. DO NOT check any condition (except compile time flag) before adding a preference.
      */
     fun getPreferenceHierarchy(context: Context): PreferenceHierarchy
+
+    /**
+     * Returns the [Intent] to show current preference screen.
+     *
+     * @param metadata the preference to locate when show the screen
+     */
+    fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?): Intent? = null
 }
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..d75f3e8 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,8 +236,9 @@
                     preference.bindRecursively(preferenceBindingFactory, preferences, storages)
                 } else {
                     preferences[preference.key]?.let {
-                        preferenceBindingFactory.getPreferenceBinding(it)?.bind(preference, it)
-                        (it as? PersistentPreference<*>)?.storage(context)?.let { storage ->
+                        preferenceBindingFactory.bind(preference, it)
+                        val metadata = it.metadata
+                        (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage ->
                             preference.preferenceDataStore =
                                 storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) }
                         }
@@ -250,10 +246,5 @@
                 }
             }
         }
-
-        private fun PreferenceBindingFactory.bind(
-            preference: Preference,
-            metadata: PreferenceMetadata?,
-        ) = metadata?.let { getPreferenceBinding(it)?.bind(preference, it) }
     }
 }
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-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index b76b028..4d33466 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -82,7 +82,7 @@
     <string name="speed_label_very_fast" msgid="8215718029533182439">"Veoma brzo"</string>
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Isteklo"</string>
     <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
-    <string name="bluetooth_disconnected" msgid="7739366554710388701">"Isključen"</string>
+    <string name="bluetooth_disconnected" msgid="7739366554710388701">"Nije povezano"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prekidanje veze…"</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Povezivanje…"</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"Povezano<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</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-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml
index 038800c..00ea04d 100644
--- a/packages/SettingsLib/res/values-in/arrays.xml
+++ b/packages/SettingsLib/res/values-in/arrays.xml
@@ -29,7 +29,7 @@
     <item msgid="4613015005934755724">"Terhubung"</item>
     <item msgid="3763530049995655072">"Ditangguhkan"</item>
     <item msgid="7852381437933824454">"Memutus sambungan..."</item>
-    <item msgid="5046795712175415059">"Sambungan terputus"</item>
+    <item msgid="5046795712175415059">"Tidak terhubung"</item>
     <item msgid="2473654476624070462">"Gagal"</item>
     <item msgid="9146847076036105115">"Diblokir"</item>
     <item msgid="4543924085816294893">"Menghindari sambungan buruk untuk sementara"</item>
@@ -43,7 +43,7 @@
     <item msgid="1043944043827424501">"Terhubung ke <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
     <item msgid="7445993821842009653">"Ditangguhkan"</item>
     <item msgid="1175040558087735707">"Diputus dari <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
-    <item msgid="699832486578171722">"Sambungan terputus"</item>
+    <item msgid="699832486578171722">"Tidak terhubung"</item>
     <item msgid="522383512264986901">"Gagal"</item>
     <item msgid="3602596701217484364">"Diblokir"</item>
     <item msgid="1999413958589971747">"Menghindari sambungan buruk untuk sementara"</item>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 3b265f8..bfd8f39 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -50,7 +50,7 @@
     <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
     <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
     <string name="wifi_remembered" msgid="3266709779723179188">"Disimpan"</string>
-    <string name="wifi_disconnected" msgid="7054450256284661757">"Terputus"</string>
+    <string name="wifi_disconnected" msgid="7054450256284661757">"Tidak terhubung"</string>
     <string name="wifi_disabled_generic" msgid="2651916945380294607">"Nonaktif"</string>
     <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Kegagalan Konfigurasi IP"</string>
     <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Masalah autentikasi"</string>
@@ -82,7 +82,7 @@
     <string name="speed_label_very_fast" msgid="8215718029533182439">"Sangat Cepat"</string>
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Sudah tidak berlaku"</string>
     <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
-    <string name="bluetooth_disconnected" msgid="7739366554710388701">"Sambungan terputus"</string>
+    <string name="bluetooth_disconnected" msgid="7739366554710388701">"Tidak terhubung"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Memutus sambungan..."</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Menghubungkan…"</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"Terhubung<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 2fb036f..785bf43 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -258,7 +258,7 @@
     <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодунун сканерин колдонуп, жаңы түзмөктөрдү жупташтырыңыз"</string>
     <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Түзмөктү атайын код аркылуу жупташтыруу"</string>
     <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Жаңы түзмөктөрдү алты сандан турган код аркылуу жупташтырасыз"</string>
-    <string name="adb_paired_devices_title" msgid="5268997341526217362">"Жупташтырылган түзмөктөр"</string>
+    <string name="adb_paired_devices_title" msgid="5268997341526217362">"Байланышкан түзмөктөр"</string>
     <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Учурда туташып турган түзмөктөр"</string>
     <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Түзмөктүн чоо-жайы"</string>
     <string name="adb_device_forget" msgid="193072400783068417">"Унутулсун"</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-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index f5e84a2..b9d970d 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -85,7 +85,7 @@
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"Не е поврзано"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Се исклучува..."</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Се поврзува..."</string>
-    <string name="bluetooth_connected" msgid="8065345572198502293">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected" msgid="8065345572198502293">"Поврзано<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_pairing" msgid="4269046942588193600">"Се спарува..."</string>
     <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без телефон)"</string>
     <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без аудиовизуелни содржини)"</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..ae1df21 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -82,7 +82,7 @@
     <string name="speed_label_very_fast" msgid="8215718029533182439">"ଅତି ଦ୍ରୁତ"</string>
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ମିଆଦ ଶେଷ ହୋଇଯାଇଛି"</string>
     <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
-    <string name="bluetooth_disconnected" msgid="7739366554710388701">"ବିଛିନ୍ନ ହେଲା"</string>
+    <string name="bluetooth_disconnected" msgid="7739366554710388701">"ଡିସକନେକ୍ଟ ହୋଇଛି"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"ବିଚ୍ଛିନ୍ନ କରୁଛି…"</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"କନେକ୍ଟ ହେଉଛି…"</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"ସଂଯୁକ୍ତ ହେଲା<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
@@ -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-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 74c294b..4c41b7d 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -82,7 +82,7 @@
     <string name="speed_label_very_fast" msgid="8215718029533182439">"Veľmi rýchla"</string>
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vypršalo"</string>
     <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
-    <string name="bluetooth_disconnected" msgid="7739366554710388701">"Odpojený"</string>
+    <string name="bluetooth_disconnected" msgid="7739366554710388701">"Odpojené"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prebieha odpájanie..."</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Prebieha pripájanie…"</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"Pripojené k zariadeniu <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</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/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 1c1117d..1d7f62a 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -82,7 +82,7 @@
     <string name="speed_label_very_fast" msgid="8215718029533182439">"Mycket snabb"</string>
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Har upphört att gälla"</string>
     <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
-    <string name="bluetooth_disconnected" msgid="7739366554710388701">"Kopplas ifrån"</string>
+    <string name="bluetooth_disconnected" msgid="7739366554710388701">"Frånkopplad"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Kopplar ifrån…"</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Ansluter…"</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"Ansluten<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 57b8f59..060b7b2 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -82,7 +82,7 @@
     <string name="speed_label_very_fast" msgid="8215718029533182439">"เร็วมาก"</string>
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"หมดอายุแล้ว"</string>
     <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
-    <string name="bluetooth_disconnected" msgid="7739366554710388701">"ตัดการเชื่อมต่อ"</string>
+    <string name="bluetooth_disconnected" msgid="7739366554710388701">"ยกเลิกการเชื่อมต่อแล้ว"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"กำลังตัดการเชื่อมต่อ..."</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"กำลังเชื่อมต่อ…"</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"เชื่อมต่อแล้ว<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 744e97e..1998d0c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,7 +1,6 @@
 package com.android.settingslib;
 
 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL;
-import static android.webkit.Flags.updateServiceV2;
 
 import android.annotation.ColorInt;
 import android.app.admin.DevicePolicyManager;
@@ -496,7 +495,7 @@
                 || packageName.equals(sServicesSystemSharedLibPackageName)
                 || packageName.equals(sSharedSystemSharedLibPackageName)
                 || packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME)
-                || (updateServiceV2() && packageName.equals(getDefaultWebViewPackageName(pm)))
+                || packageName.equals(getDefaultWebViewPackageName(pm))
                 || isDeviceProvisioningPackage(resources, packageName);
     }
 
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/DeviceSettingId.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
index e7c7476..f4b79db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
@@ -120,4 +120,10 @@
 
     /** Device setting ID for ANC. */
     int DEVICE_SETTING_ID_ANC = 1001;
+
+    /** Device setting expandable ID 1. */
+    int DEVICE_SETTING_ID_EXPANDABLE_1 = 3001;
+
+    /** Device setting expandable ID 2. */
+    int DEVICE_SETTING_ID_EXPANDABLE_2 = 3100;
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
index 8537897..c68dbee 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
@@ -106,26 +106,40 @@
 
     private fun DeviceSettingsConfig.toModel(): DeviceSettingConfigModel =
         DeviceSettingConfigModel(
-            mainItems = mainContentItems.map { it.toModel() },
-            moreSettingsItems = moreSettingsItems.map { it.toModel() },
+            mainItems = mainContentItems.toModel(),
+            moreSettingsItems = moreSettingsItems.toModel(),
             moreSettingsHelpItem = moreSettingsHelpItem?.toModel(),
         )
 
-    private fun DeviceSettingItem.toModel(): DeviceSettingConfigItemModel {
+    private fun List<DeviceSettingItem>.toModel(): List<DeviceSettingConfigItemModel> {
+        return this.flatMap { item ->
+            if (item.settingId in EXPANDABLE_SETTING_IDS) {
+                IntRange(item.settingId, item.settingId + SETTING_ID_EXPAND_LIMIT - 1).map {
+                    item.toModel(overrideSettingId = it)
+                }
+            } else {
+                listOf(item.toModel())
+            }
+        }
+    }
+
+    private fun DeviceSettingItem.toModel(
+        overrideSettingId: Int? = null
+    ): DeviceSettingConfigItemModel {
         return if (!TextUtils.isEmpty(preferenceKey)) {
             if (settingId == DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES) {
                 BluetoothProfilesItem(
-                    settingId,
+                    overrideSettingId ?: settingId,
                     highlighted,
                     preferenceKey!!,
                     extras.getStringArrayList(DeviceSettingContract.INVISIBLE_PROFILES)
                         ?: emptyList(),
                 )
             } else {
-                CommonBuiltinItem(settingId, highlighted, preferenceKey!!)
+                CommonBuiltinItem(overrideSettingId ?: settingId, highlighted, preferenceKey!!)
             }
         } else {
-            AppProvidedItem(settingId, highlighted)
+            AppProvidedItem(overrideSettingId ?: settingId, highlighted)
         }
     }
 
@@ -134,6 +148,7 @@
             is DeviceSettingIntentAction -> DeviceSettingActionModel.IntentAction(this.intent)
             is DeviceSettingPendingIntentAction ->
                 DeviceSettingActionModel.PendingIntentAction(this.pendingIntent)
+
             else -> null
         }
 
@@ -150,7 +165,7 @@
                     summary = pref.summary,
                     icon = pref.icon?.let { DeviceSettingIcon.BitmapIcon(it) },
                     isAllowedChangingState = pref.isAllowedChangingState,
-                    action = pref.action?.toModel(),
+                    action = pref.action.toModel(),
                     switchState =
                         if (pref.hasSwitch()) {
                             DeviceSettingStateModel.ActionSwitchPreferenceState(pref.checked)
@@ -163,6 +178,7 @@
                         }
                     },
                 )
+
             is MultiTogglePreference ->
                 DeviceSettingModel.MultiTogglePreference(
                     cachedDevice = cachedDevice,
@@ -178,21 +194,33 @@
                         }
                     },
                 )
+
             is DeviceSettingFooterPreference ->
                 DeviceSettingModel.FooterPreference(
                     cachedDevice = cachedDevice,
                     id = settingId,
                     footerText = pref.footerText,
                 )
+
             is DeviceSettingHelpPreference ->
                 DeviceSettingModel.HelpPreference(
                     cachedDevice = cachedDevice,
                     id = settingId,
                     intent = pref.intent,
                 )
+
             else -> DeviceSettingModel.Unknown(cachedDevice, settingId)
         }
 
     private fun ToggleInfo.toModel(): ToggleModel =
         ToggleModel(label, DeviceSettingIcon.BitmapIcon(icon))
+
+    companion object {
+        private val EXPANDABLE_SETTING_IDS =
+            listOf(
+                DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_1,
+                DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_2,
+            )
+        private const val SETTING_ID_EXPAND_LIMIT = 15
+    }
 }
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/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index 4e62fd3b..3d4e449 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -167,6 +167,26 @@
     }
 
     @Test
+    fun getDeviceSettingsConfig_expandable_success() {
+        testScope.runTest {
+            setUpConfigService(true, DEVICE_SETTING_CONFIG_EXPANDABLE)
+            `when`(settingProviderService1.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
+            `when`(settingProviderService2.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
+
+            val config = underTest.getDeviceSettingsConfig(cachedDevice)!!
+
+            assertThat(config.mainItems.map { it.settingId }).isEqualTo(
+                IntRange(
+                    DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_1,
+                    DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_1 + 14
+                ).toList()
+            )
+        }
+    }
+
+    @Test
     fun getDeviceSettingsConfig_noMetadata_returnNull() {
         testScope.runTest {
             `when`(
@@ -510,6 +530,13 @@
                 SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
                 SETTING_PROVIDER_SERVICE_INTENT_ACTION_2,
             )
+        val DEVICE_SETTING_APP_PROVIDED_ITEM_EXPANDABLE =
+            DeviceSettingItem(
+                DeviceSettingId.DEVICE_SETTING_ID_EXPANDABLE_1,
+                SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
+                SETTING_PROVIDER_SERVICE_CLASS_NAME_1,
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_1,
+            )
         val DEVICE_SETTING_BUILT_IN_ITEM =
             DeviceSettingItem(
                 DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_AUDIO_DEVICE_TYPE_GROUP,
@@ -581,5 +608,13 @@
                 listOf(DEVICE_SETTING_APP_PROVIDED_ITEM_2),
                 DEVICE_SETTING_HELP_ITEM,
             )
+        val DEVICE_SETTING_CONFIG_EXPANDABLE =
+            DeviceSettingsConfig(
+                listOf(
+                    DEVICE_SETTING_APP_PROVIDED_ITEM_EXPANDABLE,
+                ),
+                listOf(),
+                DEVICE_SETTING_HELP_ITEM,
+            )
     }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 509b88b..558ccaf 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -263,5 +263,6 @@
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_ALL, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, ANY_STRING_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 1659c9e..c2beaa8 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -944,7 +944,8 @@
                         Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
                         Settings.System.WEAR_TTS_PREWARM_ENABLED,
                         Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
-                        Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
+                        Settings.System.MULTI_AUDIO_FOCUS_ENABLED, // form-factor/OEM specific
+                        Settings.System.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME // internal cache
                 );
         if (!Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
             settings.add(Settings.System.MIN_REFRESH_RATE);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 05c5e5d..1919572 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -951,6 +951,9 @@
     <!-- Permission required for CTS test - CtsAppTestCases -->
     <uses-permission android:name="android.permission.KILL_UID" />
 
+    <!-- Permission required for CTS test - CtsTelephonyTestCases -->
+    <uses-permission android:name="android.permission.READ_BASIC_PHONE_STATE" />
+
     <application
         android:label="@string/app_label"
         android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp
index ff1a756..90e1808 100644
--- a/packages/StatementService/Android.bp
+++ b/packages/StatementService/Android.bp
@@ -35,6 +35,7 @@
     privileged: true,
     certificate: "platform",
     static_libs: [
+        "StatementServiceParser",
         "androidx.appcompat_appcompat",
         "androidx.collection_collection-ktx",
         "androidx.work_work-runtime",
diff --git a/packages/StatementService/src/com/android/statementservice/network/retriever/StatementParser.kt b/packages/StatementService/src/com/android/statementservice/network/retriever/StatementParser.kt
index 455e8085..ad137400 100644
--- a/packages/StatementService/src/com/android/statementservice/network/retriever/StatementParser.kt
+++ b/packages/StatementService/src/com/android/statementservice/network/retriever/StatementParser.kt
@@ -28,6 +28,8 @@
 import java.util.ArrayList
 import com.android.statementservice.retriever.WebAsset
 import com.android.statementservice.retriever.AndroidAppAsset
+import com.android.statementservice.retriever.DynamicAppLinkComponent
+import org.json.JSONObject
 
 /**
  * Parses JSON from the Digital Asset Links specification. For examples, see [WebAsset],
@@ -97,13 +99,45 @@
                 FIELD_NOT_ARRAY_FORMAT_STRING.format(StatementUtils.ASSET_DESCRIPTOR_FIELD_RELATION)
             )
         val target = AssetFactory.create(targetObject)
+        val dynamicAppLinkComponents = parseDynamicAppLinkComponents(
+            statement.optJSONObject(StatementUtils.ASSET_DESCRIPTOR_FIELD_RELATION_EXTENSIONS)
+        )
 
         val statements = (0 until relations.length())
             .map { relations.getString(it) }
             .map(Relation::create)
-            .map { Statement.create(source, target, it) }
+            .map { Statement.create(source, target, it, dynamicAppLinkComponents) }
         return Result.Success(ParsedStatement(statements, listOfNotNull(delegate)))
     }
 
+    private fun parseDynamicAppLinkComponents(
+        statement: JSONObject?
+    ): List<DynamicAppLinkComponent> {
+        val relationExtensions = statement?.optJSONObject(
+            StatementUtils.ASSET_DESCRIPTOR_FIELD_RELATION_EXTENSIONS
+        ) ?: return emptyList()
+        val handleAllUrlsRelationExtension = relationExtensions.optJSONObject(
+            StatementUtils.RELATION.toString()
+        ) ?: return emptyList()
+        val components = handleAllUrlsRelationExtension.optJSONArray(
+            StatementUtils.RELATION_EXTENSION_FIELD_DAL_COMPONENTS
+        ) ?: return emptyList()
+
+        return (0 until components.length())
+            .map { components.getJSONObject(it) }
+            .map { parseComponent(it) }
+    }
+
+    private fun parseComponent(component: JSONObject): DynamicAppLinkComponent {
+        val query = component.optJSONObject("?")
+        return DynamicAppLinkComponent.create(
+            component.optBoolean("exclude", false),
+            component.optString("#"),
+            component.optString("/"),
+            query?.keys()?.asSequence()?.associateWith { query.getString(it) },
+            component.optString("comments")
+        )
+    }
+
     data class ParsedStatement(val statements: List<Statement>, val delegates: List<String>)
 }
diff --git a/packages/StatementService/src/com/android/statementservice/retriever/DynamicAppLinkComponent.java b/packages/StatementService/src/com/android/statementservice/retriever/DynamicAppLinkComponent.java
new file mode 100644
index 0000000..dc27e12
--- /dev/null
+++ b/packages/StatementService/src/com/android/statementservice/retriever/DynamicAppLinkComponent.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.statementservice.retriever;
+
+import android.annotation.Nullable;
+
+import java.util.Map;
+
+/**
+ * A immutable value type representing a dynamic app link component
+ */
+public final class DynamicAppLinkComponent {
+    private final boolean mExclude;
+    private final String mFragment;
+    private final String mPath;
+    private final Map<String, String> mQuery;
+    private final String mComments;
+
+    private DynamicAppLinkComponent(boolean exclude, String fragment, String path,
+                                    Map<String, String> query, String comments) {
+        mExclude = exclude;
+        mFragment = fragment;
+        mPath = path;
+        mQuery = query;
+        mComments = comments;
+    }
+
+    /**
+     * Returns true or false indicating whether this rule should be a exclusion rule.
+     */
+    public boolean getExclude() {
+        return mExclude;
+    }
+
+    /**
+     * Returns a optional pattern string for matching URL fragments.
+     */
+    @Nullable
+    public String getFragment() {
+        return mFragment;
+    }
+
+    /**
+     * Returns a optional pattern string for matching URL paths.
+     */
+    @Nullable
+    public String getPath() {
+        return mPath;
+    }
+
+    /**
+     * Returns a optional pattern string for matching a single key-value pair in the URL query
+     * params.
+     */
+    @Nullable
+    public Map<String, String> getQuery() {
+        return mQuery;
+    }
+
+    /**
+     * Returns a optional comment string for this component.
+     */
+    @Nullable
+    public String getComments() {
+        return mComments;
+    }
+
+    /**
+     * Creates a new DynamicAppLinkComponent object.
+     */
+    public static DynamicAppLinkComponent create(boolean exclude, String fragment, String path,
+                                                 Map<String, String> query, String comments) {
+        return new DynamicAppLinkComponent(exclude, fragment, path, query, comments);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        DynamicAppLinkComponent rule = (DynamicAppLinkComponent) o;
+
+        if (mExclude != rule.mExclude) {
+            return false;
+        }
+        if (!mFragment.equals(rule.mFragment)) {
+            return false;
+        }
+        if (!mPath.equals(rule.mPath)) {
+            return false;
+        }
+        if (!mQuery.equals(rule.mQuery)) {
+            return false;
+        }
+        if (!mComments.equals(rule.mComments)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Boolean.hashCode(mExclude);
+        result = 31 * result + mFragment.hashCode();
+        result = 31 * result + mPath.hashCode();
+        result = 31 * result + mQuery.hashCode();
+        result = 31 * result + mComments.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder statement = new StringBuilder();
+        statement.append("HandleAllUriRule: ");
+        statement.append(mExclude);
+        statement.append(", ");
+        statement.append(mFragment);
+        statement.append(", ");
+        statement.append(mPath);
+        statement.append(", ");
+        statement.append(mQuery);
+        statement.append(", ");
+        statement.append(mComments);
+        return statement.toString();
+    }
+}
diff --git a/packages/StatementService/src/com/android/statementservice/retriever/JsonParser.java b/packages/StatementService/src/com/android/statementservice/retriever/JsonParser.java
index ce063ea..7635e82 100644
--- a/packages/StatementService/src/com/android/statementservice/retriever/JsonParser.java
+++ b/packages/StatementService/src/com/android/statementservice/retriever/JsonParser.java
@@ -46,12 +46,6 @@
         while (reader.hasNext()) {
             String fieldName = reader.nextName();
 
-            if (output.has(fieldName)) {
-                errorMsg = "Duplicate field name.";
-                reader.skipValue();
-                continue;
-            }
-
             JsonToken token = reader.peek();
             if (token.equals(JsonToken.BEGIN_ARRAY)) {
                 output.put(fieldName, new JSONArray(parseArray(reader)));
diff --git a/packages/StatementService/src/com/android/statementservice/retriever/Statement.java b/packages/StatementService/src/com/android/statementservice/retriever/Statement.java
index f8bab3e..b5e2046 100644
--- a/packages/StatementService/src/com/android/statementservice/retriever/Statement.java
+++ b/packages/StatementService/src/com/android/statementservice/retriever/Statement.java
@@ -23,6 +23,10 @@
 
 import kotlin.coroutines.Continuation;
 
+import java.util.Collections;
+import java.util.List;
+
+
 /**
  * An immutable value type representing a statement, consisting of a source, target, and relation.
  * This reflects an assertion that the relation holds for the source, target pair. For example, if a
@@ -32,7 +36,21 @@
  * {
  * "relation": ["delegate_permission/common.handle_all_urls"],
  * "target"  : {"namespace": "android_app", "package_name": "com.example.app",
- *              "sha256_cert_fingerprints": ["00:11:22:33"] }
+ *              "sha256_cert_fingerprints": ["00:11:22:33"] },
+ * "relation_extensions": {
+ *     "delegate_permission/common_handle_all_urls": {
+ *         "dynamic_app_link_components": [
+ *             {
+ *                 "/": "/foo*",
+ *                 "exclude": true,
+ *                 "comments": "App should not handle paths that start with foo"
+ *             },
+ *             {
+ *                 "/": "*",
+ *                 "comments": "Catch all other paths"
+ *             }
+ *         ]
+ *     }
  * }
  * </pre>
  *
@@ -40,7 +58,7 @@
  * return a {@link Statement} with {@link #getSource} equal to the input parameter,
  * {@link #getRelation} equal to
  *
- * <pre>Relation.create("delegate_permission", "common.get_login_creds");</pre>
+ * <pre>Relation.create("delegate_permission", "common.handle_all_urls");</pre>
  *
  * and with {@link #getTarget} equal to
  *
@@ -48,17 +66,23 @@
  *                           + "\"package_name\": \"com.example.app\"}"
  *                           + "\"sha256_cert_fingerprints\": \"[\"00:11:22:33\"]\"}");
  * </pre>
+ *
+ * If extensions exist for the handle_all_urls relation then {@link #getDynamicAppLinkComponents}
+ * will return a list of parsed {@link DynamicAppLinkComponent}s.
  */
 public final class Statement {
 
     private final AbstractAsset mTarget;
     private final Relation mRelation;
     private final AbstractAsset mSource;
+    private final List<DynamicAppLinkComponent> mDynamicAppLinkComponents;
 
-    private Statement(AbstractAsset source, AbstractAsset target, Relation relation) {
+    private Statement(AbstractAsset source, AbstractAsset target, Relation relation,
+                      List<DynamicAppLinkComponent> components) {
         mSource = source;
         mTarget = target;
         mRelation = relation;
+        mDynamicAppLinkComponents = Collections.unmodifiableList(components);
     }
 
     /**
@@ -86,6 +110,14 @@
     }
 
     /**
+     * Returns the relation matching rules of the statement.
+     */
+    @NonNull
+    public List<DynamicAppLinkComponent> getDynamicAppLinkComponents() {
+        return mDynamicAppLinkComponents;
+    }
+
+    /**
      * Creates a new Statement object for the specified target asset and relation. For example:
      * <pre>
      *   Asset asset = Asset.Factory.create(
@@ -95,8 +127,9 @@
      * </pre>
      */
     public static Statement create(@NonNull AbstractAsset source, @NonNull AbstractAsset target,
-                                   @NonNull Relation relation) {
-        return new Statement(source, target, relation);
+                                   @NonNull Relation relation,
+                                   @NonNull List<DynamicAppLinkComponent> components) {
+        return new Statement(source, target, relation, components);
     }
 
     @Override
@@ -119,6 +152,9 @@
         if (!mSource.equals(statement.mSource)) {
             return false;
         }
+        if (!mDynamicAppLinkComponents.equals(statement.mDynamicAppLinkComponents)) {
+            return false;
+        }
 
         return true;
     }
@@ -128,6 +164,7 @@
         int result = mTarget.hashCode();
         result = 31 * result + mRelation.hashCode();
         result = 31 * result + mSource.hashCode();
+        result = 31 * result + mDynamicAppLinkComponents.hashCode();
         return result;
     }
 
@@ -140,6 +177,10 @@
         statement.append(mTarget);
         statement.append(", ");
         statement.append(mRelation);
+        if (!mDynamicAppLinkComponents.isEmpty()) {
+            statement.append(", ");
+            statement.append(mDynamicAppLinkComponents);
+        }
         return statement.toString();
     }
 }
diff --git a/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt b/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt
index 4837aad..47c69b4 100644
--- a/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt
+++ b/packages/StatementService/src/com/android/statementservice/utils/StatementUtils.kt
@@ -17,8 +17,17 @@
 package com.android.statementservice.utils
 
 import android.content.Context
+import android.content.UriRelativeFilter
+import android.content.UriRelativeFilter.FRAGMENT
+import android.content.UriRelativeFilter.PATH
+import android.content.UriRelativeFilter.QUERY
+import android.content.UriRelativeFilterGroup
+import android.content.UriRelativeFilterGroup.ACTION_ALLOW
+import android.content.UriRelativeFilterGroup.ACTION_BLOCK
 import android.content.pm.PackageManager
 import android.util.Patterns
+import com.android.statementservice.parser.parseMatchingExpression
+import com.android.statementservice.retriever.DynamicAppLinkComponent
 import com.android.statementservice.retriever.Relation
 import java.net.URL
 import java.security.MessageDigest
@@ -52,7 +61,9 @@
      */
     const val ASSET_DESCRIPTOR_FIELD_RELATION = "relation"
     const val ASSET_DESCRIPTOR_FIELD_TARGET = "target"
+    const val ASSET_DESCRIPTOR_FIELD_RELATION_EXTENSIONS = "relation_extensions"
     const val DELEGATE_FIELD_DELEGATE = "include"
+    const val RELATION_EXTENSION_FIELD_DAL_COMPONENTS = "dynamic_app_link_components"
 
     val HEX_DIGITS =
         charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
@@ -160,4 +171,23 @@
     // Hosts with *. for wildcard subdomain support are verified against their root domain
     fun createWebAssetString(host: String) =
         WEB_ASSET_FORMAT.format(URL("https", host.removePrefix("*."), "").toString())
+
+    fun createUriRelativeFilterGroup(component: DynamicAppLinkComponent): UriRelativeFilterGroup {
+        val group = UriRelativeFilterGroup(if (component.exclude) ACTION_BLOCK else ACTION_ALLOW)
+        component.fragment?.let {
+            val (type, filter) = parseMatchingExpression(it)
+            group.addUriRelativeFilter(UriRelativeFilter(FRAGMENT, type, filter))
+        }
+        component.path?.let {
+            val (type, filter) = parseMatchingExpression(it)
+            group.addUriRelativeFilter(UriRelativeFilter(PATH, type, filter))
+        }
+        component.query?.let {
+            for ((k, v) in it) {
+                val (type, filter) = parseMatchingExpression(k + "=" + v)
+                group.addUriRelativeFilter(UriRelativeFilter(QUERY, type, filter))
+            }
+        }
+        return group
+    }
 }
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 8ddd922..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",
     ],
 }
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 510c9b7..f0e1b43 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -30,6 +30,7 @@
     <uses-permission android:name="android.permission.READ_WALLPAPER_INTERNAL" />
 
     <!-- Used to read storage for all users -->
+    <uses-permission android:name="android.permission.STORAGE_INTERNAL" />
     <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
     <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
@@ -489,7 +490,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 +500,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"/>
@@ -1017,6 +1016,10 @@
             android:exported="false">
         </activity>
 
+        <service
+            android:name="com.android.systemui.communal.widgets.GlanceableHubWidgetManagerService"
+            android:exported="false" />
+
         <!-- Doze with notifications, run in main sysui process for every user  -->
         <service
             android:name=".doze.DozeService"
@@ -1139,5 +1142,11 @@
                 android:name="android.service.dream"
                 android:resource="@xml/home_controls_dream_metadata" />
         </service>
+
+        <service android:name="com.android.systemui.dreams.homecontrols.system.HomeControlsRemoteService"
+            android:singleUser="true"
+            android:exported="false"
+            />
+
     </application>
 </manifest>
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 d124c02..5c5edb1 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -26,6 +26,13 @@
 }
 
 flag {
+   name: "modes_ui_dialog_paging"
+   namespace: "systemui"
+   description: "Add pagination to the Modes dialog in quick settings."
+   bug: "376450983"
+}
+
+flag {
    name: "priority_people_section"
    namespace: "systemui"
    description: "Add a new section for priority people (aka important conversations)."
@@ -59,7 +66,7 @@
 flag {
    name: "notification_over_expansion_clipping_fix"
    namespace: "systemui"
-   description: "fix NSSL clipping when over-expanding; fixes split shade bug."
+   description: "Fix NSSL clipping when over-expanding; fixes split shade bug."
    bug: "288553572"
    metadata {
         purpose: PURPOSE_BUGFIX
@@ -67,6 +74,14 @@
 }
 
 flag {
+    name: "notification_add_x_on_hover_to_dismiss"
+    namespace: "systemui"
+    description: "Adds an x to notifications which shows up on mouse hover, allowing the user to "
+        "dismiss a notification with mouse."
+    bug: "376297472"
+}
+
+flag {
     name: "notification_async_group_header_inflation"
     namespace: "systemui"
     description: "Inflates the notification group summary header views from the background thread."
@@ -740,10 +755,13 @@
 }
 
 flag {
-    name: "smartspace_swipe_event_logging"
+    name: "smartspace_swipe_event_logging_fix"
     namespace: "systemui"
     description: "Log card swipe events in smartspace"
     bug: "374150422"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+   }
 }
 
 flag {
@@ -908,16 +926,6 @@
 }
 
 flag {
-    name: "delayed_wakelock_release_on_background_thread"
-    namespace: "systemui"
-    description: "Released delayed wakelocks on background threads to avoid janking screen transitions."
-    bug: "316128516"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "notify_power_manager_user_activity_background"
     namespace: "systemui"
     description: "Decide whether to notify the user activity to power manager in the background thread."
@@ -1153,6 +1161,13 @@
 }
 
 flag {
+  name: "communal_hub_on_mobile"
+  namespace: "systemui"
+  description: "Brings the glanceable hub experience to mobile phones"
+  bug: "375689917"
+}
+
+flag {
     name: "dream_overlay_updated_font"
     namespace: "systemui"
     description: "Flag to enable updated font settings for dream overlay"
@@ -1625,6 +1640,16 @@
 }
 
 flag {
+  name: "home_controls_dream_hsum"
+  namespace: "systemui"
+  description: "Enables the home controls dream in HSUM"
+  bug: "370691405"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
    name: "only_show_media_stream_slider_in_single_volume_mode"
    namespace: "systemui"
    description: "When the device is in single volume mode, only show media stream slider and hide all other stream (e.g. call, notification, alarm, etc) sliders in volume panel"
@@ -1652,6 +1677,13 @@
 }
 
 flag {
+    name: "bouncer_ui_revamp"
+    namespace: "systemui"
+    description: "Updates to background (blur), button animations and font changes."
+    bug: "376491880"
+}
+
+flag {
   name: "ensure_enr_views_visibility"
   namespace: "systemui"
   description: "Ensures public and private visibilities"
@@ -1667,3 +1699,12 @@
   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/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/common/ui/compose/Icon.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt
index 6e83124..82d1436 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt
@@ -32,11 +32,7 @@
  * Note: You can use [Color.Unspecified] to disable the tint and keep the original icon colors.
  */
 @Composable
-fun Icon(
-    icon: Icon,
-    modifier: Modifier = Modifier,
-    tint: Color = LocalContentColor.current,
-) {
+fun Icon(icon: Icon, modifier: Modifier = Modifier, tint: Color = LocalContentColor.current) {
     val contentDescription = icon.contentDescription?.load()
     when (icon) {
         is Icon.Loaded -> {
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 5cb45e5..94c18cd 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
@@ -16,11 +16,13 @@
 
 package com.android.systemui.notifications.ui.composable
 
+import androidx.compose.foundation.gestures.FlingBehavior
 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.OnStopScope
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
 import com.android.compose.nestedscroll.ScrollController
 
@@ -43,6 +45,7 @@
     isCurrentGestureOverscroll: () -> Boolean,
     onStart: (Float) -> Unit = {},
     onStop: (Float) -> Unit = {},
+    flingBehavior: FlingBehavior,
 ): PriorityNestedScrollConnection {
     return PriorityNestedScrollConnection(
         orientation = Orientation.Vertical,
@@ -77,8 +80,9 @@
                     return amountConsumed
                 }
 
-                override suspend fun onStop(initialVelocity: Float): Float {
-                    onStop(initialVelocity)
+                override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
+                    val consumedByScroll = flingToScroll(initialVelocity, flingBehavior)
+                    onStop(initialVelocity - consumedByScroll)
                     if (scrimOffset() < minScrimOffset()) {
                         animateScrimOffset(minScrimOffset())
                     }
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 e1b74a9..d8abfd7 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
@@ -18,7 +18,9 @@
 
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollableDefaults
 import androidx.compose.foundation.layout.offset
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
@@ -30,6 +32,7 @@
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastCoerceAtLeast
+import com.android.compose.nestedscroll.OnStopScope
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
 import com.android.compose.nestedscroll.ScrollController
 import kotlin.math.max
@@ -46,32 +49,35 @@
     val screenHeight =
         with(LocalDensity.current) { LocalConfiguration.current.screenHeightDp.dp.toPx() }
     val overscrollOffset = remember { Animatable(0f) }
-    val stackNestedScrollConnection = remember {
-        NotificationStackNestedScrollConnection(
-            stackOffset = { overscrollOffset.value },
-            canScrollForward = canScrollForward,
-            onScroll = { offsetAvailable ->
-                coroutineScope.launch {
-                    val maxProgress = screenHeight * 0.2f
-                    val tilt = 3f
-                    var offset =
-                        overscrollOffset.value +
-                            maxProgress * tanh(x = offsetAvailable / (maxProgress * tilt))
-                    offset = max(offset, -1f * maxProgress)
-                    overscrollOffset.snapTo(offset)
-                }
-            },
-            onStop = { velocityAvailable ->
-                coroutineScope.launch {
-                    overscrollOffset.animateTo(
-                        targetValue = 0f,
-                        initialVelocity = velocityAvailable,
-                        animationSpec = tween(),
-                    )
-                }
-            },
-        )
-    }
+    val flingBehavior = ScrollableDefaults.flingBehavior()
+    val stackNestedScrollConnection =
+        remember(flingBehavior) {
+            NotificationStackNestedScrollConnection(
+                stackOffset = { overscrollOffset.value },
+                canScrollForward = canScrollForward,
+                onScroll = { offsetAvailable ->
+                    coroutineScope.launch {
+                        val maxProgress = screenHeight * 0.2f
+                        val tilt = 3f
+                        var offset =
+                            overscrollOffset.value +
+                                maxProgress * tanh(x = offsetAvailable / (maxProgress * tilt))
+                        offset = max(offset, -1f * maxProgress)
+                        overscrollOffset.snapTo(offset)
+                    }
+                },
+                onStop = { velocityAvailable ->
+                    coroutineScope.launch {
+                        overscrollOffset.animateTo(
+                            targetValue = 0f,
+                            initialVelocity = velocityAvailable,
+                            animationSpec = tween(),
+                        )
+                    }
+                },
+                flingBehavior = flingBehavior,
+            )
+        }
 
     return this.then(
         Modifier.nestedScroll(stackNestedScrollConnection).offset {
@@ -86,6 +92,7 @@
     onStart: (Float) -> Unit = {},
     onScroll: (Float) -> Unit,
     onStop: (Float) -> Unit = {},
+    flingBehavior: FlingBehavior,
 ): PriorityNestedScrollConnection {
     return PriorityNestedScrollConnection(
         orientation = Orientation.Vertical,
@@ -106,8 +113,9 @@
                     return consumed
                 }
 
-                override suspend fun onStop(initialVelocity: Float): Float {
-                    onStop(initialVelocity)
+                override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
+                    val consumedByScroll = flingToScroll(initialVelocity, flingBehavior)
+                    onStop(initialVelocity - consumedByScroll)
                     return initialVelocity
                 }
 
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 4fa1984..ae273d8 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
@@ -25,6 +25,7 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollableDefaults
 import androidx.compose.foundation.gestures.animateScrollBy
 import androidx.compose.foundation.gestures.rememberScrollableState
 import androidx.compose.foundation.gestures.scrollBy
@@ -451,12 +452,14 @@
         }
     }
 
+    val flingBehavior = ScrollableDefaults.flingBehavior()
     val scrimNestedScrollConnection =
         shadeSession.rememberSession(
             scrimOffset,
             maxScrimTop,
             minScrimTop,
             isCurrentGestureOverscroll,
+            flingBehavior,
         ) {
             NotificationScrimNestedScrollConnection(
                 scrimOffset = { scrimOffset.value },
@@ -469,6 +472,7 @@
                 contentHeight = { stackHeight.intValue.toFloat() },
                 minVisibleScrimHeight = minVisibleScrimHeight,
                 isCurrentGestureOverscroll = { isCurrentGestureOverscroll.value },
+                flingBehavior = flingBehavior,
             )
         }
 
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/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 6f1349f..46f5ecd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -45,11 +45,13 @@
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
+import com.android.systemui.res.R
 
 /** Renders a lightweight shade UI container, as an overlay. */
 @Composable
@@ -104,13 +106,11 @@
 @Composable
 private fun Modifier.panelSize(): Modifier {
     val widthSizeClass = LocalWindowSizeClass.current.widthSizeClass
-
     return this.then(
-        when (widthSizeClass) {
-            WindowWidthSizeClass.Compact -> Modifier.fillMaxWidth()
-            WindowWidthSizeClass.Medium -> Modifier.width(OverlayShade.Dimensions.PanelWidthMedium)
-            WindowWidthSizeClass.Expanded -> Modifier.width(OverlayShade.Dimensions.PanelWidthLarge)
-            else -> error("Unsupported WindowWidthSizeClass \"$widthSizeClass\"")
+        if (widthSizeClass == WindowWidthSizeClass.Compact) {
+            Modifier.fillMaxWidth()
+        } else {
+            Modifier.width(dimensionResource(id = R.dimen.shade_panel_width))
         }
     )
 }
@@ -121,14 +121,15 @@
     val systemBars = WindowInsets.systemBarsIgnoringVisibility
     val displayCutout = WindowInsets.displayCutout
     val waterfall = WindowInsets.waterfall
-    val contentPadding = PaddingValues(all = OverlayShade.Dimensions.ScrimContentPadding)
+    val horizontalPadding =
+        PaddingValues(horizontal = dimensionResource(id = R.dimen.shade_panel_margin_horizontal))
 
     val combinedPadding =
         combinePaddings(
             systemBars.asPaddingValues(),
             displayCutout.asPaddingValues(),
             waterfall.asPaddingValues(),
-            contentPadding,
+            horizontalPadding,
         )
 
     return if (widthSizeClass == WindowWidthSizeClass.Compact) {
@@ -174,10 +175,7 @@
     }
 
     object Dimensions {
-        val ScrimContentPadding = 16.dp
         val PanelCornerRadius = 46.dp
-        val PanelWidthMedium = 390.dp
-        val PanelWidthLarge = 474.dp
         val OverscrollLimit = 32.dp
     }
 
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..581fb9d 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.getSliderHapticsViewModelFactory(),
             )
 
             ExpandButton(
@@ -143,6 +142,8 @@
                             onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
                             onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
                             sliderColors = sliderColors,
+                            hapticsViewModelFactory =
+                                sliderViewModel.getSliderHapticsViewModelFactory(),
                         )
                     }
                 }
@@ -181,7 +182,7 @@
             colors =
                 IconButtonDefaults.filledIconButtonColors(
                     containerColor = sliderColors.indicatorColor,
-                    contentColor = sliderColors.iconColor
+                    contentColor = sliderColors.iconColor,
                 ),
         ) {
             Icon(
@@ -211,9 +212,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 +285,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..3718b10 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.getSliderHapticsViewModelFactory(),
             )
         }
     }
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..97ce429 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
@@ -49,6 +51,10 @@
 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 +65,38 @@
     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? =
+        hapticsViewModelFactory?.let {
+            rememberViewModel(traceName = "SliderHapticsViewModel") {
+                it.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,
+                    ),
+                )
+            }
+        }
+
+    // Perform haptics due to UI composition
+    hapticsViewModel?.onValueChange(value)
+
     PlatformSlider(
         modifier =
             modifier.sysuiResTag(state.label).clearAndSetSemantics {
@@ -94,7 +130,7 @@
                     val newValue =
                         (value + targetDirection * state.a11yStep).coerceIn(
                             state.valueRange.start,
-                            state.valueRange.endInclusive
+                            state.valueRange.endInclusive,
                         )
                     onValueChange(newValue)
                     true
@@ -102,16 +138,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 +166,8 @@
                     disabledMessage = state.disabledMessage,
                 )
             }
-        }
+        },
+        interactionSource = interactionSource,
     )
 }
 
@@ -150,14 +189,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 7c7202a..7872ffa 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
@@ -27,6 +27,7 @@
 import com.android.compose.animation.scene.content.Content
 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.OnStopScope
 import com.android.compose.nestedscroll.PriorityNestedScrollConnection
 import com.android.compose.nestedscroll.ScrollController
 import kotlin.math.absoluteValue
@@ -749,7 +750,7 @@
             return dragController.onDrag(delta = deltaScroll)
         }
 
-        override suspend fun onStop(initialVelocity: Float): Float {
+        override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
             return dragController
                 .onStop(velocity = initialVelocity, canChangeContent = canChangeScene)
                 .invoke()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index f14622f..63c5d7a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -48,7 +48,6 @@
 import androidx.compose.ui.util.fastCoerceIn
 import androidx.compose.ui.util.fastForEachReversed
 import androidx.compose.ui.util.lerp
-import com.android.compose.animation.scene.Element.State
 import com.android.compose.animation.scene.content.Content
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.transformation.PropertyTransformation
@@ -163,7 +162,7 @@
     transitionStates: List<TransitionState>,
 ): Modifier {
     fun isSharedElement(
-        stateByContent: Map<ContentKey, State>,
+        stateByContent: Map<ContentKey, Element.State>,
         transition: TransitionState.Transition,
     ): Boolean {
         fun inFromContent() = transition.fromContent in stateByContent
@@ -1281,14 +1280,14 @@
                 checkNotNull(if (currentContent == toContent) toState else fromState)
             val idleValue = contentValue(overscrollState)
             val targetValue =
-                propertySpec.transform(
-                    layoutImpl,
-                    currentContent,
-                    element,
-                    overscrollState,
-                    transition,
-                    idleValue,
-                )
+                with(propertySpec) {
+                    layoutImpl.propertyTransformationScope.transform(
+                        currentContent,
+                        element.key,
+                        transition,
+                        idleValue,
+                    )
+                }
 
             // Make sure we don't read progress if values are the same and we don't need to
             // interpolate, so we don't invalidate the phase where this is read.
@@ -1376,24 +1375,26 @@
         val idleValue = contentValue(contentState)
         val isEntering = content == toContent
         val previewTargetValue =
-            previewTransformation.transform(
-                layoutImpl,
-                content,
-                element,
-                contentState,
-                transition,
-                idleValue,
-            )
+            with(previewTransformation) {
+                layoutImpl.propertyTransformationScope.transform(
+                    content,
+                    element.key,
+                    transition,
+                    idleValue,
+                )
+            }
 
         val targetValueOrNull =
-            transformation?.transform(
-                layoutImpl,
-                content,
-                element,
-                contentState,
-                transition,
-                idleValue,
-            )
+            transformation?.let { transformation ->
+                with(transformation) {
+                    layoutImpl.propertyTransformationScope.transform(
+                        content,
+                        element.key,
+                        transition,
+                        idleValue,
+                    )
+                }
+            }
 
         // Make sure we don't read progress if values are the same and we don't need to interpolate,
         // so we don't invalidate the phase where this is read.
@@ -1460,7 +1461,14 @@
 
     val idleValue = contentValue(contentState)
     val targetValue =
-        transformation.transform(layoutImpl, content, element, contentState, transition, idleValue)
+        with(transformation) {
+            layoutImpl.propertyTransformationScope.transform(
+                content,
+                element.key,
+                transition,
+                idleValue,
+            )
+        }
 
     // Make sure we don't read progress if values are the same and we don't need to interpolate, so
     // we don't invalidate the phase where this is read.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index b00c8ad..8a6a0d6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -70,6 +70,7 @@
             // The predictive back APIs will automatically animate the progress for us in this case
             // so there is no need to animate it.
             cancelSpec = snap(),
+            animationScope = layoutImpl.animationScope,
         )
     }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index d58d3f24..e93cf8f7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -129,6 +129,7 @@
     private val verticalDraggableHandler: DraggableHandlerImpl
 
     internal val elementStateScope = ElementStateScopeImpl(this)
+    internal val propertyTransformationScope = PropertyTransformationScopeImpl(this)
     private var _userActionDistanceScope: UserActionDistanceScope? = null
     internal val userActionDistanceScope: UserActionDistanceScope
         get() =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
index 690c809..8457481 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -18,6 +18,8 @@
 
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import com.android.compose.animation.scene.transformation.PropertyTransformationScope
 
 internal class ElementStateScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) :
     ElementStateScope {
@@ -46,3 +48,15 @@
     override val fontScale: Float
         get() = layoutImpl.density.fontScale
 }
+
+internal class PropertyTransformationScopeImpl(private val layoutImpl: SceneTransitionLayoutImpl) :
+    PropertyTransformationScope, ElementStateScope by layoutImpl.elementStateScope {
+    override val density: Float
+        get() = layoutImpl.density.density
+
+    override val fontScale: Float
+        get() = layoutImpl.density.fontScale
+
+    override val layoutDirection: LayoutDirection
+        get() = layoutImpl.layoutDirection
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index c5a3067c..5936d25 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -18,10 +18,8 @@
 
 import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.content.state.TransitionState
 
 /** Anchor the size of an element to the size of another element. */
@@ -31,19 +29,15 @@
     private val anchorWidth: Boolean,
     private val anchorHeight: Boolean,
 ) : PropertyTransformation<IntSize> {
-    override fun transform(
-        layoutImpl: SceneTransitionLayoutImpl,
+    override fun PropertyTransformationScope.transform(
         content: ContentKey,
-        element: Element,
-        stateInContent: Element.State,
+        element: ElementKey,
         transition: TransitionState.Transition,
         value: IntSize,
     ): IntSize {
         fun anchorSizeIn(content: ContentKey): IntSize {
             val size =
-                layoutImpl.elements[anchor]?.stateByContent?.get(content)?.targetSize?.takeIf {
-                    it != Element.SizeUnspecified
-                }
+                anchor.targetSize(content)
                     ?: throwMissingAnchorException(
                         transformation = "AnchoredSize",
                         anchor = anchor,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 86e06ab..0a59dfe 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -17,12 +17,9 @@
 package com.android.compose.animation.scene.transformation
 
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.isSpecified
 import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.content.state.TransitionState
 
 /** Anchor the translation of an element to another element. */
@@ -30,11 +27,9 @@
     override val matcher: ElementMatcher,
     private val anchor: ElementKey,
 ) : PropertyTransformation<Offset> {
-    override fun transform(
-        layoutImpl: SceneTransitionLayoutImpl,
+    override fun PropertyTransformationScope.transform(
         content: ContentKey,
-        element: Element,
-        stateInContent: Element.State,
+        element: ElementKey,
         transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
@@ -46,18 +41,13 @@
             )
         }
 
-        val anchor = layoutImpl.elements[anchor] ?: throwException(content = null)
-        fun anchorOffsetIn(content: ContentKey): Offset? {
-            return anchor.stateByContent[content]?.targetOffset?.takeIf { it.isSpecified }
-        }
-
         // [element] will move the same amount as [anchor] does.
         // TODO(b/290184746): Also support anchors that are not shared but translated because of
         // other transformations, like an edge translation.
         val anchorFromOffset =
-            anchorOffsetIn(transition.fromContent) ?: throwException(transition.fromContent)
+            anchor.targetOffset(transition.fromContent) ?: throwException(transition.fromContent)
         val anchorToOffset =
-            anchorOffsetIn(transition.toContent) ?: throwException(transition.toContent)
+            anchor.targetOffset(transition.toContent) ?: throwException(transition.toContent)
         val offset = anchorToOffset - anchorFromOffset
 
         return if (content == transition.toContent) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index 7f86479..7223dad 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -18,10 +18,9 @@
 
 import androidx.compose.ui.geometry.Offset
 import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.Scale
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.content.state.TransitionState
 
 /**
@@ -35,11 +34,9 @@
     private val pivot: Offset = Offset.Unspecified,
 ) : PropertyTransformation<Scale> {
 
-    override fun transform(
-        layoutImpl: SceneTransitionLayoutImpl,
+    override fun PropertyTransformationScope.transform(
         content: ContentKey,
-        element: Element,
-        stateInContent: Element.State,
+        element: ElementKey,
         transition: TransitionState.Transition,
         value: Scale,
     ): Scale {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 031f50e..4ae07c5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -19,9 +19,8 @@
 import androidx.compose.ui.geometry.Offset
 import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.content.state.TransitionState
 
 /** Translate an element from an edge of the layout. */
@@ -30,21 +29,18 @@
     private val edge: Edge,
     private val startsOutsideLayoutBounds: Boolean = true,
 ) : PropertyTransformation<Offset> {
-    override fun transform(
-        layoutImpl: SceneTransitionLayoutImpl,
+    override fun PropertyTransformationScope.transform(
         content: ContentKey,
-        element: Element,
-        stateInContent: Element.State,
+        element: ElementKey,
         transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
-        val sceneSize = layoutImpl.content(content).targetSize
-        val elementSize = stateInContent.targetSize
-        if (elementSize == Element.SizeUnspecified) {
-            return value
-        }
+        val sceneSize =
+            content.targetSize()
+                ?: error("Content ${content.debugName} does not have a target size")
+        val elementSize = element.targetSize(content) ?: return value
 
-        return when (edge.resolve(layoutImpl.layoutDirection)) {
+        return when (edge.resolve(layoutDirection)) {
             Edge.Resolved.Top ->
                 if (startsOutsideLayoutBounds) {
                     Offset(value.x, -elementSize.height.toFloat())
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index 078aa0f..c11ec97 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -17,18 +17,15 @@
 package com.android.compose.animation.scene.transformation
 
 import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.content.state.TransitionState
 
 /** Fade an element in or out. */
 internal class Fade(override val matcher: ElementMatcher) : PropertyTransformation<Float> {
-    override fun transform(
-        layoutImpl: SceneTransitionLayoutImpl,
+    override fun PropertyTransformationScope.transform(
         content: ContentKey,
-        element: Element,
-        stateInContent: Element.State,
+        element: ElementKey,
         transition: TransitionState.Transition,
         value: Float,
     ): Float {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index 5f3fdaf..a159a5b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -18,9 +18,8 @@
 
 import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.content.state.TransitionState
 import kotlin.math.roundToInt
 
@@ -33,11 +32,9 @@
     private val width: Float = 1f,
     private val height: Float = 1f,
 ) : PropertyTransformation<IntSize> {
-    override fun transform(
-        layoutImpl: SceneTransitionLayoutImpl,
+    override fun PropertyTransformationScope.transform(
         content: ContentKey,
-        element: Element,
-        stateInContent: Element.State,
+        element: ElementKey,
         transition: TransitionState.Transition,
         value: IntSize,
     ): IntSize {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index de7f418..d38067d 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -18,13 +18,15 @@
 
 import androidx.compose.animation.core.Easing
 import androidx.compose.animation.core.LinearEasing
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.util.fastCoerceAtLeast
 import androidx.compose.ui.util.fastCoerceAtMost
 import androidx.compose.ui.util.fastCoerceIn
 import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.ElementStateScope
 import com.android.compose.animation.scene.content.state.TransitionState
 
 /** A transformation applied to one or more elements during a transition. */
@@ -56,22 +58,30 @@
 ) : Transformation
 
 /** A transformation that changes the value of an element property, like its size or offset. */
-internal sealed interface PropertyTransformation<T> : Transformation {
+interface PropertyTransformation<T> : Transformation {
     /**
-     * Transform [value], i.e. the value of the transformed property without this transformation.
+     * Return the transformed value for the given property, i.e.:
+     * - the value at progress = 0% for elements that are entering the layout (i.e. elements in the
+     *   content we are transitioning to).
+     * - the value at progress = 100% for elements that are leaving the layout (i.e. elements in the
+     *   content we are transitioning from).
+     *
+     * The returned value will be interpolated using the [transition] progress and [value], the
+     * value of the property when we are idle.
      */
-    // TODO(b/290184746): Figure out a public API for custom transformations that don't have access
-    // to these internal classes.
-    fun transform(
-        layoutImpl: SceneTransitionLayoutImpl,
+    fun PropertyTransformationScope.transform(
         content: ContentKey,
-        element: Element,
-        stateInContent: Element.State,
+        element: ElementKey,
         transition: TransitionState.Transition,
         value: T,
     ): T
 }
 
+interface PropertyTransformationScope : Density, ElementStateScope {
+    /** The current [direction][LayoutDirection] of the layout. */
+    val layoutDirection: LayoutDirection
+}
+
 /**
  * A [PropertyTransformation] associated to a range. This is a helper class so that normal
  * implementations of [PropertyTransformation] don't have to take care of reversing their range when
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index 7014271..af0a6ed 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -21,10 +21,9 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.OverscrollScope
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.content.state.TransitionState
 
 internal class Translate(
@@ -32,15 +31,13 @@
     private val x: Dp = 0.dp,
     private val y: Dp = 0.dp,
 ) : PropertyTransformation<Offset> {
-    override fun transform(
-        layoutImpl: SceneTransitionLayoutImpl,
+    override fun PropertyTransformationScope.transform(
         content: ContentKey,
-        element: Element,
-        stateInContent: Element.State,
+        element: ElementKey,
         transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
-        return with(layoutImpl.density) { Offset(value.x + x.toPx(), value.y + y.toPx()) }
+        return Offset(value.x + x.toPx(), value.y + y.toPx())
     }
 }
 
@@ -51,11 +48,9 @@
 ) : PropertyTransformation<Offset> {
     private val cachedOverscrollScope = CachedOverscrollScope()
 
-    override fun transform(
-        layoutImpl: SceneTransitionLayoutImpl,
+    override fun PropertyTransformationScope.transform(
         content: ContentKey,
-        element: Element,
-        stateInContent: Element.State,
+        element: ElementKey,
         transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
@@ -64,7 +59,7 @@
         // that this method was invoked after performing this check.
         val overscrollProperties = transition as TransitionState.HasOverscrollProperties
         val overscrollScope =
-            cachedOverscrollScope.getFromCacheOrCompute(layoutImpl.density, overscrollProperties)
+            cachedOverscrollScope.getFromCacheOrCompute(density = this, overscrollProperties)
 
         return Offset(x = value.x + overscrollScope.x(), y = value.y + overscrollScope.y())
     }
@@ -75,7 +70,7 @@
  * [TransitionState.HasOverscrollProperties]. This helps avoid recreating a scope every frame
  * whenever an overscroll transition is computed.
  */
-private class CachedOverscrollScope() {
+private class CachedOverscrollScope {
     private var previousScope: OverscrollScope? = null
     private var previousDensity: Density? = null
     private var previousOverscrollProperties: TransitionState.HasOverscrollProperties? = null
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
index 715d979..2b33224 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
@@ -30,6 +30,8 @@
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.createSwipeAnimation
 import kotlin.coroutines.cancellation.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.collectLatest
@@ -141,6 +143,7 @@
     progress: Flow<Float>,
     commitSpec: AnimationSpec<Float>?,
     cancelSpec: AnimationSpec<Float>?,
+    animationScope: CoroutineScope? = null,
 ) {
     fun animateOffset(targetContent: T, spec: AnimationSpec<Float>?) {
         if (state.transitionState != animation.contentTransition || animation.isAnimatingOffset()) {
@@ -176,12 +179,20 @@
         }
 
         // Start the transition.
-        state.startTransition(animation.contentTransition)
+        animationScope?.launch { startTransition(state, animation, collectionJob) }
+            ?: startTransition(state, animation, collectionJob)
+    }
+}
 
-        // The transition is done. Cancel the collection in case the transition was finished because
-        // it was interrupted by another transition.
-        if (collectionJob.isActive) {
-            collectionJob.cancel()
-        }
+private suspend fun <T : ContentKey> startTransition(
+    state: MutableSceneTransitionLayoutStateImpl,
+    animation: SwipeAnimation<T>,
+    progressCollectionJob: Job,
+) {
+    state.startTransition(animation.contentTransition)
+    // The transition is done. Cancel the collection in case the transition was finished
+    // because it was interrupted by another transition.
+    if (progressCollectionJob.isActive) {
+        progressCollectionJob.cancel()
     }
 }
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 255da31..a5be4dc 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
@@ -16,6 +16,7 @@
 
 package com.android.compose.nestedscroll
 
+import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@@ -41,6 +42,7 @@
     onHeightChanged: (Float) -> Unit,
     minHeight: () -> Float,
     maxHeight: () -> Float,
+    flingBehavior: FlingBehavior,
 ): PriorityNestedScrollConnection {
     return PriorityNestedScrollConnection(
         orientation = Orientation.Vertical,
@@ -55,7 +57,15 @@
             offsetAvailable > 0 && height() < maxHeight()
         },
         canStartPostFling = { false },
-        onStart = { LargeTopAppBarScrollController(height, maxHeight, minHeight, onHeightChanged) },
+        onStart = {
+            LargeTopAppBarScrollController(
+                height = height,
+                maxHeight = maxHeight,
+                minHeight = minHeight,
+                onHeightChanged = onHeightChanged,
+                flingBehavior = flingBehavior,
+            )
+        },
     )
 }
 
@@ -64,6 +74,7 @@
     val maxHeight: () -> Float,
     val minHeight: () -> Float,
     val onHeightChanged: (Float) -> Unit,
+    val flingBehavior: FlingBehavior,
 ) : ScrollController {
     override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
         val currentHeight = height()
@@ -79,9 +90,8 @@
         return amountConsumed
     }
 
-    override suspend fun onStop(initialVelocity: Float): Float {
-        // Don't consume the velocity on pre/post fling
-        return 0f
+    override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
+        return flingToScroll(initialVelocity, flingBehavior)
     }
 
     override fun onCancel() {
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 ca44a5c..e924ebf 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,7 +16,9 @@
 
 package com.android.compose.nestedscroll
 
+import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@@ -83,7 +85,16 @@
      * @param initialVelocity The initial velocity of the scroll when stopping.
      * @return The consumed [initialVelocity] when the animation completes.
      */
-    suspend fun onStop(initialVelocity: Float): Float
+    suspend fun OnStopScope.onStop(initialVelocity: Float): Float
+}
+
+interface OnStopScope {
+    /**
+     * Emits scroll events by using the [initialVelocity] and the [FlingBehavior].
+     *
+     * @return consumed velocity
+     */
+    suspend fun flingToScroll(initialVelocity: Float, flingBehavior: FlingBehavior): Float
 }
 
 /**
@@ -307,7 +318,11 @@
         val controller = requireController(isStopping = false)
         return coroutineScope {
             try {
-                async { controller.onStop(velocity) }
+                async {
+                        with(controller) {
+                            OnStopScopeImpl(controller = controller).onStop(velocity)
+                        }
+                    }
                     // Allows others to interrupt the job.
                     .also { stoppingJob = it }
                     // Note: this can be cancelled by [interruptStopping]
@@ -336,3 +351,19 @@
         offsetScrolledBeforePriorityMode = 0f
     }
 }
+
+private class OnStopScopeImpl(private val controller: ScrollController) : OnStopScope {
+    override suspend fun flingToScroll(
+        initialVelocity: Float,
+        flingBehavior: FlingBehavior,
+    ): Float {
+        return with(flingBehavior) {
+            object : ScrollScope {
+                    override fun scrollBy(pixels: Float): Float {
+                        return controller.onScroll(pixels, NestedScrollSource.SideEffect)
+                    }
+                }
+                .performFling(initialVelocity)
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
index a406e13..e27f9b5 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.compose.nestedscroll
 
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource
@@ -29,6 +31,13 @@
     val scrollSource = testCase.scrollSource
 
     private var height = 0f
+    private val customFlingBehavior =
+        object : FlingBehavior {
+            override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+                scrollBy(initialVelocity)
+                return initialVelocity / 2f
+            }
+        }
 
     private fun buildScrollConnection(heightRange: ClosedFloatingPointRange<Float>) =
         LargeTopAppBarNestedScrollConnection(
@@ -36,6 +45,7 @@
             onHeightChanged = { height = it },
             minHeight = { heightRange.start },
             maxHeight = { heightRange.endInclusive },
+            flingBehavior = customFlingBehavior,
         )
 
     private fun NestedScrollConnection.scroll(
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 0364cdc..5442840 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
@@ -18,12 +18,15 @@
 
 package com.android.compose.nestedscroll
 
+import androidx.compose.foundation.gestures.FlingBehavior
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.ScrollScope
 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
+import com.android.compose.test.runMonotonicClockTest
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
@@ -41,7 +44,16 @@
     private var consumeScroll = true
     private var lastStop: Float? = null
     private var isCancelled: Boolean = false
-    private var consumeStop = true
+    private var onStopConsumeFlingToScroll = false
+    private var onStopConsumeAll = true
+
+    private val customFlingBehavior =
+        object : FlingBehavior {
+            override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+                scrollBy(initialVelocity)
+                return initialVelocity / 2f
+            }
+        }
 
     private val scrollConnection =
         PriorityNestedScrollConnection(
@@ -57,9 +69,16 @@
                         return if (consumeScroll) deltaScroll else 0f
                     }
 
-                    override suspend fun onStop(initialVelocity: Float): Float {
+                    override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
                         lastStop = initialVelocity
-                        return if (consumeStop) initialVelocity else 0f
+                        var velocityConsumed = 0f
+                        if (onStopConsumeFlingToScroll) {
+                            velocityConsumed = flingToScroll(initialVelocity, customFlingBehavior)
+                        }
+                        if (onStopConsumeAll) {
+                            velocityConsumed = initialVelocity
+                        }
+                        return velocityConsumed
                     }
 
                     override fun onCancel() {
@@ -178,6 +197,22 @@
     }
 
     @Test
+    fun onStopScrollUsingFlingToScroll() = runMonotonicClockTest {
+        startPriorityModePostScroll()
+        onStopConsumeFlingToScroll = true
+        onStopConsumeAll = false
+        lastScroll = Float.NaN
+
+        val consumed = scrollConnection.onPreFling(available = Velocity(2f, 2f))
+
+        assertThat(lastStop).isEqualTo(2f)
+        // flingToScroll should try to scroll the content, customFlingBehavior uses the velocity.
+        assertThat(lastScroll).isEqualTo(2f)
+        // customFlingBehavior returns half of the vertical velocity.
+        assertThat(consumed).isEqualTo(Velocity(0f, 1f))
+    }
+
+    @Test
     fun ifCannotStopOnPreFling_shouldStopOnPostFling() = runTest {
         startPriorityModePostScroll()
         canStopOnPreFling = false
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index a883977..4ed8fd8 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockReactiveSetting
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
@@ -103,7 +103,9 @@
                 view.onZenDataChanged(data)
             }
 
-            override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
+            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+                view.updateAxes(axes)
+            }
 
             override var isReactiveTouchInteractionEnabled
                 get() = view.isReactiveTouchInteractionEnabled
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index c5b7518..7014826 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -33,8 +33,8 @@
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.plugins.clocks.ClockFaceEvents
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
-import com.android.systemui.plugins.clocks.ClockReactiveSetting
 import com.android.systemui.plugins.clocks.ClockSettings
 import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
 import com.android.systemui.plugins.clocks.ThemeConfig
@@ -264,7 +264,7 @@
 
         override fun onZenDataChanged(data: ZenData) {}
 
-        override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
+        override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {}
     }
 
     open inner class DefaultClockAnimations(
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 a89e6fb..d70d61c 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
@@ -21,12 +21,12 @@
 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.ClockFontAxis
 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
@@ -91,15 +91,42 @@
             axes =
                 if (isClockReactiveVariantsEnabled)
                     listOf(
-                        ClockReactiveAxis(
-                            key = "wdth",
-                            type = AxisType.Slider,
-                            maxValue = 1000f,
-                            minValue = 100f,
+                        ClockFontAxis(
+                            key = "wght",
+                            type = AxisType.Float,
+                            minValue = 1f,
                             currentValue = 400f,
+                            maxValue = 1000f,
+                            name = "Weight",
+                            description = "Glyph Weight",
+                        ),
+                        ClockFontAxis(
+                            key = "wdth",
+                            type = AxisType.Float,
+                            minValue = 25f,
+                            currentValue = 100f,
+                            maxValue = 151f,
                             name = "Width",
                             description = "Glyph Width",
-                        )
+                        ),
+                        ClockFontAxis(
+                            key = "ROND",
+                            type = AxisType.Boolean,
+                            minValue = 0f,
+                            currentValue = 0f,
+                            maxValue = 100f,
+                            name = "Round",
+                            description = "Glyph Roundness",
+                        ),
+                        ClockFontAxis(
+                            key = "slnt",
+                            type = AxisType.Boolean,
+                            minValue = 0f,
+                            currentValue = 0f,
+                            maxValue = -10f,
+                            name = "Slant",
+                            description = "Glyph Slant",
+                        ),
                     )
                 else listOf(),
         )
@@ -121,12 +148,12 @@
                                         FontTextStyle(
                                             lineHeight = 147.25f,
                                             fontVariation =
-                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                         ),
                                     aodStyle =
                                         FontTextStyle(
                                             fontVariation =
-                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                             fillColorLight = "#FFFFFFFF",
                                             outlineColor = "#00000000",
                                             renderType = RenderType.CHANGE_WEIGHT,
@@ -147,12 +174,12 @@
                                         FontTextStyle(
                                             lineHeight = 147.25f,
                                             fontVariation =
-                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                         ),
                                     aodStyle =
                                         FontTextStyle(
                                             fontVariation =
-                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                             fillColorLight = "#FFFFFFFF",
                                             outlineColor = "#00000000",
                                             renderType = RenderType.CHANGE_WEIGHT,
@@ -173,12 +200,12 @@
                                         FontTextStyle(
                                             lineHeight = 147.25f,
                                             fontVariation =
-                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                         ),
                                     aodStyle =
                                         FontTextStyle(
                                             fontVariation =
-                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                             fillColorLight = "#FFFFFFFF",
                                             outlineColor = "#00000000",
                                             renderType = RenderType.CHANGE_WEIGHT,
@@ -199,12 +226,12 @@
                                         FontTextStyle(
                                             lineHeight = 147.25f,
                                             fontVariation =
-                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                                "'wght' 603, 'wdth' 100, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                         ),
                                     aodStyle =
                                         FontTextStyle(
                                             fontVariation =
-                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                                "'wght' 74, 'wdth' 43, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                             fillColorLight = "#FFFFFFFF",
                                             outlineColor = "#00000000",
                                             renderType = RenderType.CHANGE_WEIGHT,
@@ -229,12 +256,14 @@
                         timespec = DigitalTimespec.TIME_FULL_FORMAT,
                         style =
                             FontTextStyle(
-                                fontVariation = "'wght' 600, 'wdth' 100, 'opsz' 144, 'ROND' 100",
+                                fontVariation =
+                                    "'wght' 600, 'wdth' 100, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                 fontSizeScale = 0.98f,
                             ),
                         aodStyle =
                             FontTextStyle(
-                                fontVariation = "'wght' 133, 'wdth' 43, 'opsz' 144, 'ROND' 100",
+                                fontVariation =
+                                    "'wght' 133, 'wdth' 43, 'opsz' 144, 'ROND' 100, 'slnt' 0",
                                 fillColorLight = "#FFFFFFFF",
                                 outlineColor = "#00000000",
                                 renderType = RenderType.CHANGE_WEIGHT,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index a75022a..d8fd776 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -23,8 +23,8 @@
 import com.android.systemui.plugins.clocks.ClockConfig
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockEvents
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
-import com.android.systemui.plugins.clocks.ClockReactiveSetting
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
@@ -113,9 +113,9 @@
                 largeClock.events.onZenDataChanged(data)
             }
 
-            override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {
-                smallClock.events.onReactiveAxesChanged(axes)
-                largeClock.events.onReactiveAxesChanged(axes)
+            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+                smallClock.events.onFontAxesChanged(axes)
+                largeClock.events.onFontAxesChanged(axes)
             }
         }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 8ffc00d..a4782ac 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -32,7 +32,7 @@
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.plugins.clocks.ClockFaceEvents
 import com.android.systemui.plugins.clocks.ClockFaceLayout
-import com.android.systemui.plugins.clocks.ClockReactiveSetting
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
@@ -136,13 +136,16 @@
 
         override fun onFontSettingChanged(fontSizePx: Float) {
             layerController.faceEvents.onFontSettingChanged(fontSizePx)
+            view.requestLayout()
         }
 
         override fun onThemeChanged(theme: ThemeConfig) {
             layerController.faceEvents.onThemeChanged(theme)
         }
 
-        override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
+        override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+            layerController.events.onFontAxesChanged(axes)
+        }
 
         /**
          * targetRegion passed to all customized clock applies counter translationY of
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index 7b1960e..143b28f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.plugins.clocks.ClockEvents
 import com.android.systemui.plugins.clocks.ClockFaceConfig
 import com.android.systemui.plugins.clocks.ClockFaceEvents
-import com.android.systemui.plugins.clocks.ClockReactiveSetting
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
@@ -246,7 +246,9 @@
 
             override fun onZenDataChanged(data: ZenData) {}
 
-            override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
+            override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
+                view.updateAxes(axes)
+            }
         }
 
     override val animations =
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
index ce4d71c..b09332f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.core.MessageBuffer
 import com.android.systemui.plugins.clocks.AlarmData
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
 import com.android.systemui.shared.clocks.LogUtil
@@ -126,6 +127,11 @@
         invalidate()
     }
 
+    fun updateAxes(axes: List<ClockFontAxisSetting>) {
+        digitalClockTextViewMap.forEach { _, view -> view.updateAxes(axes) }
+        requestLayout()
+    }
+
     fun onFontSettingChanged(fontSizePx: Float) {
         digitalClockTextViewMap.forEach { _, view -> view.applyTextSize(fontSizePx) }
     }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index 25b2ad7..edcee9d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -39,8 +39,7 @@
     DigitalClockFaceView(context, messageBuffer) {
     override var digitalClockTextViewMap = mutableMapOf<Int, SimpleDigitalClockTextView>()
     val digitLeftTopMap = mutableMapOf<Int, Point>()
-    var maxSingleDigitHeight = -1
-    var maxSingleDigitWidth = -1
+    var maxSingleDigitSize = Point(-1, -1)
     val lockscreenTranslate = Point(0, 0)
     val aodTranslate = Point(0, 0)
 
@@ -119,25 +118,26 @@
     }
 
     protected override fun calculateSize(widthMeasureSpec: Int, heightMeasureSpec: Int): Point {
+        maxSingleDigitSize = Point(-1, -1)
         digitalClockTextViewMap.forEach { (_, textView) ->
             textView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
+            maxSingleDigitSize.x = max(maxSingleDigitSize.x, textView.measuredWidth)
+            maxSingleDigitSize.y = max(maxSingleDigitSize.y, textView.measuredHeight)
         }
         val textView = digitalClockTextViewMap[R.id.HOUR_FIRST_DIGIT]!!
-        maxSingleDigitHeight = textView.measuredHeight
-        maxSingleDigitWidth = textView.measuredWidth
-        aodTranslate.x = -(maxSingleDigitWidth * AOD_HORIZONTAL_TRANSLATE_RATIO).toInt()
-        aodTranslate.y = (maxSingleDigitHeight * AOD_VERTICAL_TRANSLATE_RATIO).toInt()
+        aodTranslate.x = -(maxSingleDigitSize.x * AOD_HORIZONTAL_TRANSLATE_RATIO).toInt()
+        aodTranslate.y = (maxSingleDigitSize.y * AOD_VERTICAL_TRANSLATE_RATIO).toInt()
         return Point(
-            ((maxSingleDigitWidth + abs(aodTranslate.x)) * 2),
-            ((maxSingleDigitHeight + abs(aodTranslate.y)) * 2),
+            ((maxSingleDigitSize.x + abs(aodTranslate.x)) * 2),
+            ((maxSingleDigitSize.y + abs(aodTranslate.y)) * 2),
         )
     }
 
     protected override fun calculateLeftTopPosition() {
         digitLeftTopMap[R.id.HOUR_FIRST_DIGIT] = Point(0, 0)
-        digitLeftTopMap[R.id.HOUR_SECOND_DIGIT] = Point(maxSingleDigitWidth, 0)
-        digitLeftTopMap[R.id.MINUTE_FIRST_DIGIT] = Point(0, maxSingleDigitHeight)
-        digitLeftTopMap[R.id.MINUTE_SECOND_DIGIT] = Point(maxSingleDigitWidth, maxSingleDigitHeight)
+        digitLeftTopMap[R.id.HOUR_SECOND_DIGIT] = Point(maxSingleDigitSize.x, 0)
+        digitLeftTopMap[R.id.MINUTE_FIRST_DIGIT] = Point(0, maxSingleDigitSize.y)
+        digitLeftTopMap[R.id.MINUTE_SECOND_DIGIT] = Point(maxSingleDigitSize)
         digitLeftTopMap.forEach { _, point ->
             point.x += abs(aodTranslate.x)
             point.y += abs(aodTranslate.y)
@@ -162,7 +162,7 @@
     override fun animateDoze(isDozing: Boolean, isAnimated: Boolean) {
         dozeControlState.animateDoze = {
             super.animateDoze(isDozing, isAnimated)
-            if (maxSingleDigitHeight == -1) {
+            if (maxSingleDigitSize.x < 0 || maxSingleDigitSize.y < 0) {
                 measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
             }
             digitalClockTextViewMap.forEach { (id, textView) ->
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index baed3ae..0dacce1 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.customization.R
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.shared.clocks.AssetLoader
 import com.android.systemui.shared.clocks.ClockAnimation
 import com.android.systemui.shared.clocks.DigitTranslateAnimator
@@ -140,6 +141,25 @@
         invalidate()
     }
 
+    override fun updateAxes(axes: List<ClockFontAxisSetting>) {
+        val sb = StringBuilder()
+        sb.append("'opsz' 144")
+
+        for (axis in axes) {
+            if (sb.length > 0) sb.append(", ")
+            sb.append("'${axis.key}' ${axis.value.toInt()}")
+        }
+
+        val fvar = sb.toString()
+        lockScreenPaint.typeface = typefaceCache.getTypefaceForVariant(fvar)
+        typeface = lockScreenPaint.typeface
+        textAnimator.setTextStyle(fvar = fvar, animate = true)
+        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
+        recomputeMaxSingleDigitSizes()
+        requestLayout()
+        invalidate()
+    }
+
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
         logger.d("onMeasure()")
         if (isVertical) {
@@ -189,6 +209,7 @@
                     MeasureSpec.getMode(measuredHeight),
                 )
         }
+
         if (MeasureSpec.getMode(widthMeasureSpec) == EXACTLY) {
             expectedWidth = widthMeasureSpec
         } else {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt
index 30b54d8..3d2ed4a1 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shared.clocks.view
 
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.shared.clocks.AssetLoader
 import com.android.systemui.shared.clocks.TextStyle
 
@@ -34,6 +35,8 @@
 
     fun updateColor(color: Int)
 
+    fun updateAxes(axes: List<ClockFontAxisSetting>)
+
     fun refreshTime()
 
     fun animateCharge()
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/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
index 4856f15..e142169 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
@@ -3,7 +3,9 @@
 import android.app.admin.DevicePolicyManager
 import android.app.admin.DevicePolicyResourcesManager
 import android.content.pm.UserInfo
+import android.hardware.biometrics.Flags
 import android.os.UserManager
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
@@ -33,6 +35,7 @@
 import org.mockito.junit.MockitoJUnit
 
 private const val USER_ID = 22
+private const val OWNER_ID = 10
 private const val OPERATION_ID = 100L
 private const val MAX_ATTEMPTS = 5
 
@@ -67,7 +70,7 @@
                 lockPatternUtils,
                 userManager,
                 devicePolicyManager,
-                systemClock
+                systemClock,
             )
     }
 
@@ -115,58 +118,87 @@
 
     @Test fun pinCredentialWhenBadAndThrottled() = pinCredential(badCredential(timeout = 5_000))
 
-    private fun pinCredential(result: VerifyCredentialResponse) = runTest {
-        val usedAttempts = 1
-        whenever(lockPatternUtils.getCurrentFailedPasswordAttempts(eq(USER_ID)))
-            .thenReturn(usedAttempts)
-        whenever(lockPatternUtils.verifyCredential(any(), eq(USER_ID), anyInt())).thenReturn(result)
-        whenever(lockPatternUtils.verifyGatekeeperPasswordHandle(anyLong(), anyLong(), eq(USER_ID)))
-            .thenReturn(result)
-        whenever(lockPatternUtils.setLockoutAttemptDeadline(anyInt(), anyInt())).thenAnswer {
-            systemClock.elapsedRealtime() + (it.arguments[1] as Int)
-        }
+    @EnableFlags(Flags.FLAG_PRIVATE_SPACE_BP)
+    @Test
+    fun pinCredentialTiedProfileWhenGood() = pinCredential(goodCredential(), OWNER_ID)
 
-        // wrap in an async block so the test can advance the clock if throttling credential
-        // checks prevents the method from returning
-        val statusList = mutableListOf<CredentialStatus>()
-        interactor
-            .verifyCredential(pinRequest(), LockscreenCredential.createPin("1234"))
-            .toList(statusList)
+    @EnableFlags(Flags.FLAG_PRIVATE_SPACE_BP)
+    @Test
+    fun pinCredentialTiedProfileWhenBad() = pinCredential(badCredential(), OWNER_ID)
 
-        val last = statusList.removeLastOrNull()
-        if (result.isMatched) {
-            assertThat(statusList).isEmpty()
-            val successfulResult = last as? CredentialStatus.Success.Verified
-            assertThat(successfulResult).isNotNull()
-            assertThat(successfulResult!!.hat).isEqualTo(result.gatekeeperHAT)
+    @EnableFlags(Flags.FLAG_PRIVATE_SPACE_BP)
+    @Test
+    fun pinCredentialTiedProfileWhenBadAndThrottled() =
+        pinCredential(badCredential(timeout = 5_000), OWNER_ID)
 
-            verify(lockPatternUtils).userPresent(eq(USER_ID))
-            verify(lockPatternUtils)
-                .removeGatekeeperPasswordHandle(eq(result.gatekeeperPasswordHandle))
-        } else {
-            val failedResult = last as? CredentialStatus.Fail.Error
-            assertThat(failedResult).isNotNull()
-            assertThat(failedResult!!.remainingAttempts)
-                .isEqualTo(if (result.timeout > 0) null else MAX_ATTEMPTS - usedAttempts - 1)
-            assertThat(failedResult.urgentMessage).isNull()
+    private fun pinCredential(result: VerifyCredentialResponse, credentialOwner: Int = USER_ID) =
+        runTest {
+            val usedAttempts = 1
+            whenever(lockPatternUtils.getCurrentFailedPasswordAttempts(eq(USER_ID)))
+                .thenReturn(usedAttempts)
+            whenever(lockPatternUtils.verifyCredential(any(), eq(USER_ID), anyInt()))
+                .thenReturn(result)
+            whenever(lockPatternUtils.verifyTiedProfileChallenge(any(), eq(USER_ID), anyInt()))
+                .thenReturn(result)
+            whenever(
+                    lockPatternUtils.verifyGatekeeperPasswordHandle(
+                        anyLong(),
+                        anyLong(),
+                        eq(USER_ID),
+                    )
+                )
+                .thenReturn(result)
+            whenever(lockPatternUtils.setLockoutAttemptDeadline(anyInt(), anyInt())).thenAnswer {
+                systemClock.elapsedRealtime() + (it.arguments[1] as Int)
+            }
 
-            if (result.timeout > 0) { // failed and throttled
-                // messages are in the throttled errors, so the final Error.error is empty
-                assertThat(failedResult.error).isEmpty()
-                assertThat(statusList).isNotEmpty()
-                assertThat(statusList.filterIsInstance(CredentialStatus.Fail.Throttled::class.java))
-                    .hasSize(statusList.size)
+            // wrap in an async block so the test can advance the clock if throttling credential
+            // checks prevents the method from returning
+            val statusList = mutableListOf<CredentialStatus>()
+            interactor
+                .verifyCredential(
+                    pinRequest(credentialOwner),
+                    LockscreenCredential.createPin("1234"),
+                )
+                .toList(statusList)
 
-                verify(lockPatternUtils).setLockoutAttemptDeadline(eq(USER_ID), eq(result.timeout))
-            } else { // failed
-                assertThat(failedResult.error)
-                    .matches(Regex("(.*)try again(.*)", RegexOption.IGNORE_CASE).toPattern())
+            val last = statusList.removeLastOrNull()
+            if (result.isMatched) {
                 assertThat(statusList).isEmpty()
+                val successfulResult = last as? CredentialStatus.Success.Verified
+                assertThat(successfulResult).isNotNull()
+                assertThat(successfulResult!!.hat).isEqualTo(result.gatekeeperHAT)
 
-                verify(lockPatternUtils).reportFailedPasswordAttempt(eq(USER_ID))
+                verify(lockPatternUtils).userPresent(eq(USER_ID))
+                verify(lockPatternUtils)
+                    .removeGatekeeperPasswordHandle(eq(result.gatekeeperPasswordHandle))
+            } else {
+                val failedResult = last as? CredentialStatus.Fail.Error
+                assertThat(failedResult).isNotNull()
+                assertThat(failedResult!!.remainingAttempts)
+                    .isEqualTo(if (result.timeout > 0) null else MAX_ATTEMPTS - usedAttempts - 1)
+                assertThat(failedResult.urgentMessage).isNull()
+
+                if (result.timeout > 0) { // failed and throttled
+                    // messages are in the throttled errors, so the final Error.error is empty
+                    assertThat(failedResult.error).isEmpty()
+                    assertThat(statusList).isNotEmpty()
+                    assertThat(
+                            statusList.filterIsInstance(CredentialStatus.Fail.Throttled::class.java)
+                        )
+                        .hasSize(statusList.size)
+
+                    verify(lockPatternUtils)
+                        .setLockoutAttemptDeadline(eq(USER_ID), eq(result.timeout))
+                } else { // failed
+                    assertThat(failedResult.error)
+                        .matches(Regex("(.*)try again(.*)", RegexOption.IGNORE_CASE).toPattern())
+                    assertThat(statusList).isEmpty()
+
+                    verify(lockPatternUtils).reportFailedPasswordAttempt(eq(USER_ID))
+                }
             }
         }
-    }
 
     @Test
     fun pinCredentialWhenBadAndFinalAttempt() = runTest {
@@ -212,11 +244,11 @@
     }
 }
 
-private fun pinRequest(): BiometricPromptRequest.Credential.Pin =
+private fun pinRequest(credentialOwner: Int = USER_ID): BiometricPromptRequest.Credential.Pin =
     BiometricPromptRequest.Credential.Pin(
         promptInfo(),
-        BiometricUserInfo(USER_ID),
-        BiometricOperationInfo(OPERATION_ID)
+        BiometricUserInfo(userId = USER_ID, deviceCredentialOwnerId = credentialOwner),
+        BiometricOperationInfo(OPERATION_ID),
     )
 
 private fun goodCredential(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index dc499cd..b39a888 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -77,6 +77,7 @@
     private val fingerprintRepository = FakeFingerprintPropertyRepository()
     private val promptRepository = FakePromptRepository()
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
+    private val credentialInteractor = FakeCredentialInteractor()
 
     private lateinit var displayStateRepository: FakeDisplayStateRepository
     private lateinit var displayRepository: FakeDisplayRepository
@@ -99,8 +100,9 @@
             PromptSelectorInteractorImpl(
                 fingerprintRepository,
                 displayStateInteractor,
+                credentialInteractor,
                 promptRepository,
-                lockPatternUtils
+                lockPatternUtils,
             )
     }
 
@@ -134,13 +136,13 @@
         testScope.runTest {
             useBiometricsAndReset(
                 allowCredentialFallback = true,
-                setComponentNameForConfirmDeviceCredentialActivity = true
+                setComponentNameForConfirmDeviceCredentialActivity = true,
             )
         }
 
     private fun TestScope.useBiometricsAndReset(
         allowCredentialFallback: Boolean,
-        setComponentNameForConfirmDeviceCredentialActivity: Boolean = false
+        setComponentNameForConfirmDeviceCredentialActivity: Boolean = false,
     ) {
         setUserCredentialType(isPassword = true)
 
@@ -357,7 +359,7 @@
 
     private fun setPrompt(
         info: PromptInfo = basicPromptInfo(),
-        onSwitchToCredential: Boolean = false
+        onSwitchToCredential: Boolean = false,
     ) {
         interactor.setPrompt(
             info,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 55fd344..c803097 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -28,7 +28,6 @@
 import android.graphics.Rect
 import android.graphics.drawable.BitmapDrawable
 import android.hardware.biometrics.BiometricFingerprintConstants
-import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
 import android.hardware.biometrics.PromptContentItemBulletedText
 import android.hardware.biometrics.PromptContentView
 import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
@@ -163,7 +162,7 @@
             naturalDisplayWidth = 1000,
             naturalDisplayHeight = 3000,
             scaleFactor = 1f,
-            rotation = if (isLandscape) Surface.ROTATION_90 else Surface.ROTATION_0
+            rotation = if (isLandscape) Surface.ROTATION_90 else Surface.ROTATION_0,
         )
 
     private lateinit var promptContentView: PromptContentView
@@ -180,21 +179,21 @@
         whenever(
                 kosmos.packageManager.getApplicationInfo(
                     eq(OP_PACKAGE_NAME_WITH_APP_LOGO),
-                    anyInt()
+                    anyInt(),
                 )
             )
             .thenReturn(applicationInfoWithIconAndDescription)
         whenever(
                 kosmos.packageManager.getApplicationInfo(
                     eq(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO),
-                    anyInt()
+                    anyInt(),
                 )
             )
             .thenReturn(applicationInfoWithIconAndDescription)
         whenever(
                 kosmos.packageManager.getApplicationInfo(
                     eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND),
-                    anyInt()
+                    anyInt(),
                 )
             )
             .thenThrow(NameNotFoundException())
@@ -220,13 +219,13 @@
         overrideResource(logoResFromApp, logoDrawableFromAppRes)
         overrideResource(
             R.array.config_useActivityLogoForBiometricPrompt,
-            arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO)
+            arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO),
         )
 
         overrideResource(R.dimen.biometric_dialog_fingerprint_icon_width, mockFingerprintIconWidth)
         overrideResource(
             R.dimen.biometric_dialog_fingerprint_icon_height,
-            mockFingerprintIconHeight
+            mockFingerprintIconHeight,
         )
         overrideResource(R.dimen.biometric_dialog_face_icon_size, mockFaceIconSize)
 
@@ -243,7 +242,7 @@
                 it.sensorType.toSensorType(),
                 it.allLocations.associateBy { sensorLocationInternal ->
                     sensorLocationInternal.displayId
-                }
+                },
             )
         }
 
@@ -441,7 +440,7 @@
 
                 kosmos.promptViewModel.showAuthenticated(
                     modality = testCase.authenticatedModality,
-                    dismissAfterDelay = DELAY
+                    dismissAfterDelay = DELAY,
                 )
 
                 // SFPS test cases
@@ -513,7 +512,7 @@
                 kosmos.promptViewModel.showAuthenticated(
                     modality = testCase.authenticatedModality,
                     dismissAfterDelay = DELAY,
-                    "TEST"
+                    "TEST",
                 )
 
                 if (testCase.isFingerprintOnly) {
@@ -558,7 +557,7 @@
 
                 kosmos.promptViewModel.showAuthenticated(
                     modality = testCase.authenticatedModality,
-                    dismissAfterDelay = DELAY
+                    dismissAfterDelay = DELAY,
                 )
 
                 if (testCase.isFaceOnly) {
@@ -598,7 +597,7 @@
 
                 kosmos.promptViewModel.showAuthenticated(
                     modality = testCase.authenticatedModality,
-                    dismissAfterDelay = DELAY
+                    dismissAfterDelay = DELAY,
                 )
 
                 kosmos.promptViewModel.confirmAuthenticated()
@@ -701,7 +700,7 @@
                     .isEqualTo(
                         Pair(
                             expectedUdfpsOverlayParams.sensorBounds.width(),
-                            expectedUdfpsOverlayParams.sensorBounds.height()
+                            expectedUdfpsOverlayParams.sensorBounds.height(),
                         )
                     )
             } else {
@@ -834,7 +833,7 @@
 
                 kosmos.promptViewModel.showAuthenticated(
                     modality = testCase.authenticatedModality,
-                    dismissAfterDelay = DELAY
+                    dismissAfterDelay = DELAY,
                 )
 
                 kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
@@ -907,10 +906,7 @@
                 }
             )
 
-        assertButtonsVisible(
-            cancel = expectConfirmation,
-            confirm = expectConfirmation,
-        )
+        assertButtonsVisible(cancel = expectConfirmation, confirm = expectConfirmation)
     }
 
     @Test
@@ -1158,10 +1154,7 @@
 
         testScheduler.runCurrent()
         assertThat(messages)
-            .containsExactly(
-                PromptMessage.Empty,
-                PromptMessage.Error(expectedErrorMessage),
-            )
+            .containsExactly(PromptMessage.Empty, PromptMessage.Error(expectedErrorMessage))
             .inOrder()
 
         testScheduler.advanceUntilIdle()
@@ -1221,10 +1214,7 @@
         assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
         if (expectConfirmation) {
             assertThat(size).isEqualTo(PromptSize.MEDIUM)
-            assertButtonsVisible(
-                cancel = true,
-                confirm = true,
-            )
+            assertButtonsVisible(cancel = true, confirm = true)
 
             kosmos.promptViewModel.confirmAuthenticated()
             assertThat(message).isEqualTo(PromptMessage.Empty)
@@ -1251,10 +1241,7 @@
         assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
         if (expectConfirmation) {
             assertThat(size).isEqualTo(PromptSize.MEDIUM)
-            assertButtonsVisible(
-                cancel = true,
-                confirm = true,
-            )
+            assertButtonsVisible(cancel = true, confirm = true)
 
             if (testCase.modalities.hasSfps) {
                 kosmos.promptViewModel.showAuthenticated(BiometricModality.Fingerprint, 0)
@@ -1290,10 +1277,7 @@
         if (expectConfirmation) {
             if (testCase.isFaceOnly) {
                 assertThat(size).isEqualTo(PromptSize.MEDIUM)
-                assertButtonsVisible(
-                    cancel = true,
-                    confirm = true,
-                )
+                assertButtonsVisible(cancel = true, confirm = true)
 
                 kosmos.promptViewModel.confirmAuthenticated()
             } else if (testCase.isCoex) {
@@ -1323,10 +1307,7 @@
         assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
         if (expectConfirmation) {
             assertThat(size).isEqualTo(PromptSize.MEDIUM)
-            assertButtonsVisible(
-                cancel = true,
-                confirm = true,
-            )
+            assertButtonsVisible(cancel = true, confirm = true)
 
             kosmos.promptViewModel.confirmAuthenticated()
             assertThat(message).isEqualTo(PromptMessage.Empty)
@@ -1398,10 +1379,7 @@
         assertThat(authenticating).isFalse()
         assertThat(authenticated?.isAuthenticated).isTrue()
         assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
-        assertButtonsVisible(
-            cancel = expectConfirmation,
-            confirm = expectConfirmation,
-        )
+        assertButtonsVisible(cancel = expectConfirmation, confirm = expectConfirmation)
     }
 
     @Test
@@ -1421,7 +1399,7 @@
                 errorMessage,
                 messageAfterError = helpMessage,
                 authenticateAfterError = false,
-                failedModality = testCase.authenticatedModality
+                failedModality = testCase.authenticatedModality,
             )
         }
 
@@ -1472,7 +1450,7 @@
 
         kosmos.promptViewModel.onAnnounceAccessibilityHint(
             obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
-            true
+            true,
         )
 
         if (testCase.modalities.hasUdfps) {
@@ -1497,14 +1475,13 @@
 
         kosmos.promptViewModel.onAnnounceAccessibilityHint(
             obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
-            true
+            true,
         )
 
         assertThat(hint.isNullOrBlank()).isTrue()
     }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun descriptionOverriddenByVerticalListContentView() =
         runGenericTest(description = "test description", contentView = promptContentView) {
             val contentView by collectLastValue(kosmos.promptViewModel.contentView)
@@ -1515,11 +1492,10 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun descriptionOverriddenByContentViewWithMoreOptionsButton() =
         runGenericTest(
             description = "test description",
-            contentView = promptContentViewWithMoreOptionsButton
+            contentView = promptContentViewWithMoreOptionsButton,
         ) {
             val contentView by collectLastValue(kosmos.promptViewModel.contentView)
             val description by collectLastValue(kosmos.promptViewModel.description)
@@ -1529,7 +1505,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun descriptionWithoutContentView() =
         runGenericTest(description = "test description") {
             val contentView by collectLastValue(kosmos.promptViewModel.contentView)
@@ -1540,7 +1515,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_nullIfPkgNameNotFound() =
         runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1549,7 +1523,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_defaultFromActivityInfo() =
         runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1564,7 +1537,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_defaultIsNull() =
         runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1573,7 +1545,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_default() = runGenericTest {
         val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
         assertThat(logoInfo).isNotNull()
@@ -1581,7 +1552,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_resSetByApp() =
         runGenericTest(logoRes = logoResFromApp) {
             val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
@@ -1591,7 +1561,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_bitmapSetByApp() =
         runGenericTest(logoBitmap = logoBitmapFromApp) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1599,7 +1568,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_emptyIfPkgNameNotFound() =
         runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1607,7 +1575,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_defaultFromActivityInfo() =
         runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1619,7 +1586,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_defaultIsEmpty() =
         runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1627,14 +1593,12 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_default() = runGenericTest {
         val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
         assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo)
     }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_setByApp() =
         runGenericTest(logoDescription = logoDescriptionFromApp) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1826,7 +1790,7 @@
         kosmos.biometricStatusRepository.setFingerprintAcquiredStatus(
             AcquiredFingerprintAuthenticationStatus(
                 AuthenticationReason.BiometricPromptAuthentication,
-                BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN
+                BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN,
             )
         )
 
@@ -1893,7 +1857,7 @@
                     fingerprint =
                         fingerprintSensorPropertiesInternal(
                                 strong = true,
-                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON,
                             )
                             .first(),
                     authenticatedModality = BiometricModality.Fingerprint,
@@ -1903,7 +1867,7 @@
                     fingerprint =
                         fingerprintSensorPropertiesInternal(
                                 strong = true,
-                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON,
                             )
                             .first(),
                     authenticatedModality = BiometricModality.Fingerprint,
@@ -1913,7 +1877,7 @@
                     fingerprint =
                         fingerprintSensorPropertiesInternal(
                                 strong = true,
-                                sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+                                sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
                             )
                             .first(),
                     authenticatedModality = BiometricModality.Fingerprint,
@@ -1932,7 +1896,7 @@
                     fingerprint =
                         fingerprintSensorPropertiesInternal(
                                 strong = true,
-                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON,
                             )
                             .first(),
                     authenticatedModality = BiometricModality.Fingerprint,
@@ -1958,7 +1922,7 @@
                     fingerprint =
                         fingerprintSensorPropertiesInternal(
                                 strong = true,
-                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON
+                                sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON,
                             )
                             .first(),
                     authenticatedModality = BiometricModality.Fingerprint,
@@ -1969,7 +1933,7 @@
                     fingerprint =
                         fingerprintSensorPropertiesInternal(
                                 strong = true,
-                                sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
+                                sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
                             )
                             .first(),
                     authenticatedModality = BiometricModality.Fingerprint,
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/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt
similarity index 98%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt
index 8ae9d2e..55d7d08 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt
@@ -73,7 +73,7 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
+class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() {
     @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
     @Mock private lateinit var providerInfoA: AppWidgetProviderInfo
     @Mock private lateinit var providerInfoB: AppWidgetProviderInfo
@@ -105,14 +105,14 @@
             "com.android.fake/WidgetProviderC",
         )
 
-    private lateinit var underTest: CommunalWidgetRepositoryImpl
+    private lateinit var underTest: CommunalWidgetRepositoryLocalImpl
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         fakeWidgets = MutableStateFlow(emptyMap())
         fakeProviders = MutableStateFlow(emptyMap())
-        logBuffer = logcatLogBuffer(name = "CommunalWidgetRepoImplTest")
+        logBuffer = logcatLogBuffer(name = "CommunalWidgetRepoLocalImplTest")
         backupUtils = CommunalBackupUtils(kosmos.applicationContext)
 
         setAppWidgetIds(emptyList())
@@ -126,7 +126,7 @@
         restoreUser(mainUser)
 
         underTest =
-            CommunalWidgetRepositoryImpl(
+            CommunalWidgetRepositoryLocalImpl(
                 appWidgetHost,
                 testScope.backgroundScope,
                 kosmos.testDispatcher,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModelTest.kt
new file mode 100644
index 0000000..c210154
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.model
+
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.os.Parcel
+import android.os.UserHandle
+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 CommunalWidgetContentModelTest : SysuiTestCase() {
+    @Test
+    fun testParcelizeAvailableWidget() {
+        val widgetToParcelize =
+            CommunalWidgetContentModel.Available(
+                appWidgetId = 1,
+                providerInfo =
+                    AppWidgetProviderInfo().apply { provider = ComponentName("pkg", "cls") },
+                rank = 2,
+                spanY = 3,
+            )
+
+        val parcel = Parcel.obtain()
+        widgetToParcelize.writeToParcel(parcel, flags = 0)
+
+        parcel.setDataPosition(0)
+
+        // Only checking fields are equal and not complete equality because not all fields are
+        // specified in the fake AppWidgetProviderInfo
+        val widgetFromParcel =
+            CommunalWidgetContentModel.createFromParcel(parcel)
+                as CommunalWidgetContentModel.Available
+        assertThat(widgetFromParcel.appWidgetId).isEqualTo(widgetToParcelize.appWidgetId)
+        assertThat(widgetFromParcel.rank).isEqualTo(widgetToParcelize.rank)
+        assertThat(widgetFromParcel.spanY).isEqualTo(widgetToParcelize.spanY)
+        assertThat(widgetFromParcel.providerInfo.provider)
+            .isEqualTo(widgetToParcelize.providerInfo.provider)
+    }
+
+    @Test
+    fun testParcelizePendingWidget() {
+        val widgetToParcelize =
+            CommunalWidgetContentModel.Pending(
+                appWidgetId = 2,
+                rank = 3,
+                componentName = ComponentName("pkg", "cls"),
+                icon = null,
+                user = UserHandle(0),
+                spanY = 6,
+            )
+
+        val parcel = Parcel.obtain()
+        widgetToParcelize.writeToParcel(parcel, flags = 0)
+
+        parcel.setDataPosition(0)
+
+        val widgetFromParcel = CommunalWidgetContentModel.createFromParcel(parcel)
+        assertThat(widgetFromParcel).isEqualTo(widgetToParcelize)
+    }
+}
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 7816d3b..bea1010 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
@@ -367,6 +367,39 @@
         }
 
     @Test
+    fun testMaxSpansLessThanCurrentSpan() =
+        testScope.runTest {
+            // heightPerSpan =
+            // (viewportHeightPx - verticalContentPaddingPx - (totalSpans - 1)
+            // * verticalItemSpacingPx) / totalSpans
+            // = 145.3333
+            // maxSpans = (maxHeightPx + verticalItemSpacing) /
+            // (heightPerSpanPx + verticalItemSpacingPx)
+            // = 4.72
+            // This is invalid because the max span calculation comes out to be less than
+            // the current span. Ensure we handle this case correctly.
+            val layout =
+                GridLayout(
+                    verticalItemSpacingPx = 100f,
+                    currentRow = 0,
+                    minHeightPx = 480,
+                    maxHeightPx = 1060,
+                    currentSpan = 6,
+                    resizeMultiple = 3,
+                    totalSpans = 6,
+                    viewportHeightPx = 1600,
+                    verticalContentPaddingPx = 228f,
+                )
+            updateGridLayout(layout)
+
+            val topState = underTest.topDragState
+            val bottomState = underTest.bottomDragState
+
+            assertThat(topState.anchors.toList()).containsExactly(0 to 0f)
+            assertThat(bottomState.anchors.toList()).containsExactly(0 to 0f, -3 to -736f)
+        }
+
+    @Test
     fun testCanExpand_atTopPosition_withMultipleAnchors_returnsTrue() =
         testScope.runTest {
             val twoRowGrid = singleSpanGrid.copy(totalSpans = 2, currentSpan = 1, currentRow = 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
index 1e79112..18513fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
 import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -61,6 +62,7 @@
                 backgroundScope = kosmos.applicationCoroutineScope,
                 hostId = 116,
                 logBuffer = logcatLogBuffer("CommunalAppWidgetHostTest"),
+                glanceableHubMultiUserHelper = kosmos.fakeGlanceableHubMultiUserHelper,
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index c9f3f14..9ef2b19 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -24,10 +24,14 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.shared.model.FakeGlanceableHubMultiUserHelper
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -58,6 +62,9 @@
     @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
     @Mock private lateinit var communalWidgetHost: CommunalWidgetHost
 
+    private lateinit var widgetManager: GlanceableHubWidgetManager
+    private lateinit var helper: FakeGlanceableHubMultiUserHelper
+
     private lateinit var appWidgetIdToRemove: MutableSharedFlow<Int>
 
     private lateinit var underTest: CommunalAppWidgetHostStartable
@@ -69,17 +76,23 @@
         kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
         mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
 
+        widgetManager = kosmos.mockGlanceableHubWidgetManager
+        helper = kosmos.fakeGlanceableHubMultiUserHelper
         appWidgetIdToRemove = MutableSharedFlow()
         whenever(appWidgetHost.appWidgetIdToRemove).thenReturn(appWidgetIdToRemove)
 
         underTest =
             CommunalAppWidgetHostStartable(
-                appWidgetHost,
-                communalWidgetHost,
-                kosmos.communalInteractor,
-                kosmos.fakeUserTracker,
+                { appWidgetHost },
+                { communalWidgetHost },
+                { kosmos.communalInteractor },
+                { kosmos.communalSettingsInteractor },
+                { kosmos.keyguardInteractor },
+                { kosmos.fakeUserTracker },
                 kosmos.applicationCoroutineScope,
                 kosmos.testDispatcher,
+                { widgetManager },
+                helper,
             )
     }
 
@@ -211,7 +224,7 @@
                 fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
                 fakeCommunalWidgetRepository.addPendingWidget(
                     appWidgetId = 2,
-                    userId = USER_INFO_WORK.id
+                    userId = USER_INFO_WORK.id,
                 )
                 fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
 
@@ -246,16 +259,42 @@
             }
         }
 
-    private suspend fun setCommunalAvailable(available: Boolean) =
+    @Test
+    fun onStartHeadlessSystemUser_registerWidgetManager_whenCommunalIsAvailable() =
+        with(kosmos) {
+            testScope.runTest {
+                helper.setIsInHeadlessSystemUser(true)
+                underTest.start()
+                runCurrent()
+                verify(widgetManager, never()).register()
+                verify(widgetManager, never()).unregister()
+
+                // Binding to the service does not require keyguard showing
+                setCommunalAvailable(true, setKeyguardShowing = false)
+                runCurrent()
+                verify(widgetManager).register()
+
+                setCommunalAvailable(false)
+                runCurrent()
+                verify(widgetManager).unregister()
+            }
+        }
+
+    private suspend fun setCommunalAvailable(
+        available: Boolean,
+        setKeyguardShowing: Boolean = true,
+    ) =
         with(kosmos) {
             fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
             fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
-            fakeKeyguardRepository.setKeyguardShowing(true)
+            if (setKeyguardShowing) {
+                fakeKeyguardRepository.setKeyguardShowing(true)
+            }
             val settingsValue = if (available) 1 else 0
             fakeSettings.putIntForUser(
                 Settings.Secure.GLANCEABLE_HUB_ENABLED,
                 settingsValue,
-                MAIN_USER_INFO.id
+                MAIN_USER_INFO.id,
             )
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
index 054e516..017c778 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
@@ -26,6 +26,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -84,7 +85,7 @@
                     any<Int>(),
                     any<UserHandle>(),
                     any<ComponentName>(),
-                    any<Bundle>()
+                    any<Bundle>(),
                 )
             )
             .thenReturn(true)
@@ -96,6 +97,7 @@
                 appWidgetHost,
                 selectedUserInteractor,
                 logcatLogBuffer("CommunalWidgetHostTest"),
+                glanceableHubMultiUserHelper = kosmos.fakeGlanceableHubMultiUserHelper,
             )
     }
 
@@ -162,7 +164,7 @@
                         any<Int>(),
                         any<UserHandle>(),
                         any<ComponentName>(),
-                        any<Bundle>()
+                        any<Bundle>(),
                     )
                 )
                 .thenReturn(false)
@@ -283,12 +285,7 @@
             // all providers are emitted at once
             assertThat(providerInfoValues).hasSize(2)
             assertThat(providerInfoValues[1])
-                .containsExactlyEntriesIn(
-                    mapOf(
-                        Pair(1, providerInfo1),
-                        Pair(2, providerInfo2),
-                    )
-                )
+                .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
         }
 
     @Test
@@ -304,12 +301,7 @@
             // Assert that the provider info map is populated
             val providerInfo by collectLastValue(underTest.appWidgetProviders)
             assertThat(providerInfo)
-                .containsExactlyEntriesIn(
-                    mapOf(
-                        Pair(1, providerInfo1),
-                        Pair(2, providerInfo2),
-                    )
-                )
+                .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
 
             // Host stop listening
             observer.onHostStopListening()
@@ -332,12 +324,7 @@
 
             // Assert that the provider info map is populated
             assertThat(providerInfo)
-                .containsExactlyEntriesIn(
-                    mapOf(
-                        Pair(1, providerInfo1),
-                        Pair(2, providerInfo2),
-                    )
-                )
+                .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
 
             // Provider info for widget 1 updated
             val listener =
@@ -349,12 +336,7 @@
 
             // Assert that the update is reflected in the flow
             assertThat(providerInfo)
-                .containsExactlyEntriesIn(
-                    mapOf(
-                        Pair(1, providerInfo3),
-                        Pair(2, providerInfo2),
-                    )
-                )
+                .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo3), Pair(2, providerInfo2)))
         }
 
     @Test
@@ -371,12 +353,7 @@
 
             // Assert that the provider info map is populated
             assertThat(providerInfo)
-                .containsExactlyEntriesIn(
-                    mapOf(
-                        Pair(1, providerInfo1),
-                        Pair(2, providerInfo2),
-                    )
-                )
+                .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
 
             // Bind a new widget
             whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(3)
@@ -388,11 +365,7 @@
             // Assert that the new provider is reflected in the flow
             assertThat(providerInfo)
                 .containsExactlyEntriesIn(
-                    mapOf(
-                        Pair(1, providerInfo1),
-                        Pair(2, providerInfo2),
-                        Pair(3, providerInfo3),
-                    )
+                    mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2), Pair(3, providerInfo3))
                 )
         }
 
@@ -410,31 +383,21 @@
 
             // Assert that the provider info map is populated
             assertThat(providerInfo)
-                .containsExactlyEntriesIn(
-                    mapOf(
-                        Pair(1, providerInfo1),
-                        Pair(2, providerInfo2),
-                    )
-                )
+                .containsExactlyEntriesIn(mapOf(Pair(1, providerInfo1), Pair(2, providerInfo2)))
 
             // Remove widget 1
             observer.onDeleteAppWidgetId(1)
             runCurrent()
 
             // Assert that provider info for widget 1 is removed
-            assertThat(providerInfo)
-                .containsExactlyEntriesIn(
-                    mapOf(
-                        Pair(2, providerInfo2),
-                    )
-                )
+            assertThat(providerInfo).containsExactlyEntriesIn(mapOf(Pair(2, providerInfo2)))
         }
 
     private fun selectUser() {
         kosmos.fakeUserRepository.selectedUser.value =
             SelectedUserModel(
                 userInfo = UserInfo(0, "Current user", 0),
-                selectionStatus = SelectionStatus.SELECTION_COMPLETE
+                selectionStatus = SelectionStatus.SELECTION_COMPLETE,
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt
new file mode 100644
index 0000000..44ce085
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceTest.kt
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Intent
+import android.os.UserHandle
+import android.testing.TestableLooper
+import android.widget.RemoteViews
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.testKosmos
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4::class)
+class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val appWidgetHostListenerCaptor = argumentCaptor<AppWidgetHost.AppWidgetHostListener>()
+
+    private val widgetRepository = kosmos.fakeCommunalWidgetRepository
+    private val appWidgetHost = mock<CommunalAppWidgetHost>()
+    private val communalWidgetHost = mock<CommunalWidgetHost>()
+    private val multiUserHelper = kosmos.fakeGlanceableHubMultiUserHelper
+
+    private lateinit var underTest: GlanceableHubWidgetManagerService
+
+    @Before
+    fun setup() {
+        underTest =
+            GlanceableHubWidgetManagerService(
+                widgetRepository,
+                appWidgetHost,
+                communalWidgetHost,
+                multiUserHelper,
+                logcatLogBuffer("GlanceableHubWidgetManagerServiceTest"),
+            )
+    }
+
+    @Test
+    fun appWidgetHost_listenWhenServiceIsBound() {
+        underTest.onCreate()
+        verify(appWidgetHost).startListening()
+        verify(communalWidgetHost).startObservingHost()
+        verify(appWidgetHost, never()).stopListening()
+        verify(communalWidgetHost, never()).stopObservingHost()
+
+        underTest.onDestroy()
+        verify(appWidgetHost).stopListening()
+        verify(communalWidgetHost).stopObservingHost()
+    }
+
+    @Test
+    fun widgetsListener_getWidgetUpdates() =
+        testScope.runTest {
+            setupWidgets()
+
+            // Bind service
+            val binder = underTest.onBind(Intent())
+            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+            // Verify the update is as expected
+            val widgets by collectLastValue(service.listenForWidgetUpdates())
+            assertThat(widgets).hasSize(3)
+            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+        }
+
+    @Test
+    fun widgetsListener_multipleListeners_eachGetsWidgetUpdates() =
+        testScope.runTest {
+            setupWidgets()
+
+            // Bind service
+            val binder = underTest.onBind(Intent())
+            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+            // Verify the update for the first listener is as expected
+            val widgets1 by collectLastValue(service.listenForWidgetUpdates())
+            assertThat(widgets1).hasSize(3)
+            assertThat(widgets1?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+            assertThat(widgets1?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets1?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+            // Verify the update for the second listener is as expected
+            val widgets2 by collectLastValue(service.listenForWidgetUpdates())
+            assertThat(widgets2).hasSize(3)
+            assertThat(widgets2?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+            assertThat(widgets2?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets2?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+        }
+
+    @Test
+    fun setAppWidgetHostListener_getUpdates() =
+        testScope.runTest {
+            // Bind service
+            val binder = underTest.onBind(Intent())
+            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+            // Set listener
+            val listener = mock<IGlanceableHubWidgetManagerService.IAppWidgetHostListener>()
+            service.setAppWidgetHostListener(1, listener)
+
+            // Verify a listener is set on the host
+            verify(appWidgetHost).setListener(eq(1), appWidgetHostListenerCaptor.capture())
+            val appWidgetHostListener = appWidgetHostListenerCaptor.firstValue
+
+            // Each update should be passed to the listener
+            val providerInfo = mock<AppWidgetProviderInfo>()
+            appWidgetHostListener.onUpdateProviderInfo(providerInfo)
+            verify(listener).onUpdateProviderInfo(providerInfo)
+
+            val remoteViews = mock<RemoteViews>()
+            appWidgetHostListener.updateAppWidget(remoteViews)
+            verify(listener).updateAppWidget(remoteViews)
+
+            appWidgetHostListener.updateAppWidgetDeferred("pkg", 1)
+            verify(listener).updateAppWidgetDeferred("pkg", 1)
+
+            appWidgetHostListener.onViewDataChanged(1)
+            verify(listener).onViewDataChanged(1)
+        }
+
+    @Test
+    fun addWidget_getWidgetUpdate() =
+        testScope.runTest {
+            setupWidgets()
+
+            // Bind service
+            val binder = underTest.onBind(Intent())
+            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+            // Verify the update is as expected
+            val widgets by collectLastValue(service.listenForWidgetUpdates())
+            assertThat(widgets).hasSize(3)
+            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+            // Add a widget
+            service.addWidget(ComponentName("pkg_4", "cls_4"), UserHandle.of(0), 3)
+            runCurrent()
+
+            // Verify an update pushed with widget 4 added
+            assertThat(widgets).hasSize(4)
+            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+            assertThat(widgets?.get(3)?.has(4, "pkg_4/cls_4", 3, 3)).isTrue()
+        }
+
+    @Test
+    fun deleteWidget_getWidgetUpdate() =
+        testScope.runTest {
+            setupWidgets()
+
+            // Bind service
+            val binder = underTest.onBind(Intent())
+            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+            // Verify the update is as expected
+            val widgets by collectLastValue(service.listenForWidgetUpdates())
+            assertThat(widgets).hasSize(3)
+            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+            // Delete a widget
+            service.deleteWidget(1)
+            runCurrent()
+
+            // Verify an update pushed with widget 1 removed
+            assertThat(widgets).hasSize(2)
+            assertThat(widgets?.get(0)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets?.get(1)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+        }
+
+    @Test
+    fun updateWidgetOrder_getWidgetUpdate() =
+        testScope.runTest {
+            setupWidgets()
+
+            // Bind service
+            val binder = underTest.onBind(Intent())
+            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+            // Verify the update is as expected
+            val widgets by collectLastValue(service.listenForWidgetUpdates())
+            assertThat(widgets).hasSize(3)
+            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+            // Update widget order
+            service.updateWidgetOrder(intArrayOf(1, 2, 3), intArrayOf(2, 1, 0))
+            runCurrent()
+
+            // Verify an update pushed with the new order
+            assertThat(widgets).hasSize(3)
+            assertThat(widgets?.get(0)?.has(3, "pkg_3/cls_3", 0, 6)).isTrue()
+            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets?.get(2)?.has(1, "pkg_1/cls_1", 2, 3)).isTrue()
+        }
+
+    @Test
+    fun resizeWidget_getWidgetUpdate() =
+        testScope.runTest {
+            setupWidgets()
+
+            // Bind service
+            val binder = underTest.onBind(Intent())
+            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
+
+            // Verify the update is as expected
+            val widgets by collectLastValue(service.listenForWidgetUpdates())
+            assertThat(widgets).hasSize(3)
+            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
+            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+
+            // Resize widget 1 from spanY 3 to 6
+            service.resizeWidget(1, 6, intArrayOf(1, 2, 3), intArrayOf(0, 1, 2))
+            runCurrent()
+
+            // Verify an update pushed with the new size for widget 1
+            assertThat(widgets).hasSize(3)
+            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 6)).isTrue()
+            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
+            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
+        }
+
+    private fun setupWidgets() {
+        widgetRepository.addWidget(
+            appWidgetId = 1,
+            componentName = "pkg_1/cls_1",
+            rank = 0,
+            spanY = 3,
+        )
+        widgetRepository.addWidget(
+            appWidgetId = 2,
+            componentName = "pkg_2/cls_2",
+            rank = 1,
+            spanY = 3,
+        )
+        widgetRepository.addWidget(
+            appWidgetId = 3,
+            componentName = "pkg_3/cls_3",
+            rank = 2,
+            spanY = 6,
+        )
+    }
+
+    private fun IGlanceableHubWidgetManagerService.listenForWidgetUpdates() =
+        conflatedCallbackFlow<List<CommunalWidgetContentModel>> {
+            val listener =
+                object : IGlanceableHubWidgetsListener.Stub() {
+                    override fun onWidgetsUpdated(widgets: List<CommunalWidgetContentModel>) {
+                        trySend(widgets)
+                    }
+                }
+            addWidgetsListener(listener)
+            awaitClose { removeWidgetsListener(listener) }
+        }
+
+    private fun CommunalWidgetContentModel.has(
+        appWidgetId: Int,
+        componentName: String,
+        rank: Int,
+        spanY: Int,
+    ): Boolean {
+        return this is CommunalWidgetContentModel.Available &&
+            this.appWidgetId == appWidgetId &&
+            this.providerInfo.provider.flattenToString() == componentName &&
+            this.rank == rank &&
+            this.spanY == spanY
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt
index 55fafdf..5d4eaf0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetConfigurationControllerTest.kt
@@ -22,6 +22,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
@@ -55,7 +56,12 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         underTest =
-            WidgetConfigurationController(ownerActivity, appWidgetHost, kosmos.testDispatcher)
+            WidgetConfigurationController(
+                ownerActivity,
+                { appWidgetHost },
+                kosmos.testDispatcher,
+                kosmos.fakeGlanceableHubMultiUserHelper,
+            )
     }
 
     @Test
@@ -68,7 +74,7 @@
                             eq(123),
                             anyInt(),
                             eq(WidgetConfigurationController.REQUEST_CODE),
-                            any()
+                            any(),
                         )
                     )
                     .thenThrow(ActivityNotFoundException())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
index 2b7e7ad..20d6615 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
@@ -16,34 +16,57 @@
 
 package com.android.systemui.deviceentry.domain.interactor
 
+import android.hardware.face.FaceManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.keyguard.keyguardUpdateMonitor
 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.biometrics.authController
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.configureKeyguardBypass
 import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.data.repository.powerRepository
 import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.phone.dozeScrimController
+import com.android.systemui.statusbar.phone.screenOffAnimationController
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -51,24 +74,52 @@
 class DeviceEntryHapticsInteractorTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val underTest = kosmos.deviceEntryHapticsInteractor
 
+    private lateinit var underTest: DeviceEntryHapticsInteractor
+
+    @Before
+    fun setup() {
+        if (SceneContainerFlag.isEnabled) {
+            whenever(kosmos.authController.isUdfpsFingerDown).thenReturn(false)
+            whenever(kosmos.dozeScrimController.isPulsing).thenReturn(false)
+            whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(true)
+            whenever(kosmos.screenOffAnimationController.isKeyguardShowDelayed()).thenReturn(false)
+
+            // Dependencies for DeviceEntrySourceInteractor#biometricUnlockStateOnKeyguardDismissed
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(true)
+
+            // Mock authenticationMethodIsSecure true
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+
+            kosmos.keyguardBouncerRepository.setAlternateVisible(false)
+            kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+        } else {
+            underTest = kosmos.deviceEntryHapticsInteractor
+        }
+    }
+
+    @DisableSceneContainer
     @Test
     fun nonPowerButtonFPS_vibrateSuccess() =
         testScope.runTest {
             val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
-            setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+            enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
             runCurrent()
-            enterDeviceFromBiometricUnlock()
+            enterDeviceFromFingerprintUnlockLegacy()
             assertThat(playSuccessHaptic).isNotNull()
         }
 
+    @DisableSceneContainer
     @Test
     fun powerButtonFPS_vibrateSuccess() =
         testScope.runTest {
             val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
-            setPowerButtonFingerprintProperty()
-            setFingerprintEnrolled()
+            enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
 
             // It's been 10 seconds since the last power button wakeup
@@ -76,16 +127,16 @@
             advanceTimeBy(10000)
             runCurrent()
 
-            enterDeviceFromBiometricUnlock()
+            enterDeviceFromFingerprintUnlockLegacy()
             assertThat(playSuccessHaptic).isNotNull()
         }
 
+    @DisableSceneContainer
     @Test
     fun powerButtonFPS_powerDown_doNotVibrateSuccess() =
         testScope.runTest {
             val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
-            setPowerButtonFingerprintProperty()
-            setFingerprintEnrolled()
+            enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             kosmos.fakeKeyEventRepository.setPowerButtonDown(true) // power button is currently DOWN
 
             // It's been 10 seconds since the last power button wakeup
@@ -93,16 +144,16 @@
             advanceTimeBy(10000)
             runCurrent()
 
-            enterDeviceFromBiometricUnlock()
+            enterDeviceFromFingerprintUnlockLegacy()
             assertThat(playSuccessHaptic).isNull()
         }
 
+    @DisableSceneContainer
     @Test
     fun powerButtonFPS_powerButtonRecentlyPressed_doNotVibrateSuccess() =
         testScope.runTest {
             val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
-            setPowerButtonFingerprintProperty()
-            setFingerprintEnrolled()
+            enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
 
             // It's only been 50ms since the last power button wakeup
@@ -110,7 +161,7 @@
             advanceTimeBy(50)
             runCurrent()
 
-            enterDeviceFromBiometricUnlock()
+            enterDeviceFromFingerprintUnlockLegacy()
             assertThat(playSuccessHaptic).isNull()
         }
 
@@ -118,7 +169,7 @@
     fun nonPowerButtonFPS_vibrateError() =
         testScope.runTest {
             val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
-            setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+            enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
             runCurrent()
             fingerprintFailure()
             assertThat(playErrorHaptic).isNotNull()
@@ -128,8 +179,8 @@
     fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() =
         testScope.runTest {
             val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
-            setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
-            coExEnrolledAndEnabled()
+            enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
+            enrollFace()
             runCurrent()
             faceFailure()
             assertThat(playErrorHaptic).isNull()
@@ -139,8 +190,7 @@
     fun powerButtonFPS_vibrateError() =
         testScope.runTest {
             val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
-            setPowerButtonFingerprintProperty()
-            setFingerprintEnrolled()
+            enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             runCurrent()
             fingerprintFailure()
             assertThat(playErrorHaptic).isNotNull()
@@ -150,15 +200,143 @@
     fun powerButtonFPS_powerDown_doNotVibrateError() =
         testScope.runTest {
             val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
-            setPowerButtonFingerprintProperty()
-            setFingerprintEnrolled()
+            enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
             kosmos.fakeKeyEventRepository.setPowerButtonDown(true)
             runCurrent()
             fingerprintFailure()
             assertThat(playErrorHaptic).isNull()
         }
 
-    private suspend fun enterDeviceFromBiometricUnlock() {
+    @EnableSceneContainer
+    @Test
+    fun playSuccessHaptic_onDeviceEntryFromUdfps_sceneContainer() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = false)
+            underTest = kosmos.deviceEntryHapticsInteractor
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
+            runCurrent()
+            configureDeviceEntryFromBiometricSource(isFpUnlock = true)
+            verifyDeviceEntryFromFingerprintAuth()
+            assertThat(playSuccessHaptic).isNotNull()
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun playSuccessHaptic_onDeviceEntryFromSfps_sceneContainer() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = false)
+            underTest = kosmos.deviceEntryHapticsInteractor
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
+            kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
+
+            // It's been 10 seconds since the last power button wakeup
+            setAwakeFromPowerButton()
+            advanceTimeBy(10000)
+            runCurrent()
+
+            configureDeviceEntryFromBiometricSource(isFpUnlock = true)
+            verifyDeviceEntryFromFingerprintAuth()
+            assertThat(playSuccessHaptic).isNotNull()
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun playSuccessHaptic_onDeviceEntryFromFaceAuth_sceneContainer() =
+        testScope.runTest {
+            enrollFace()
+            kosmos.configureKeyguardBypass(isBypassAvailable = true)
+            underTest = kosmos.deviceEntryHapticsInteractor
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            configureDeviceEntryFromBiometricSource(isFaceUnlock = true)
+            verifyDeviceEntryFromFaceAuth()
+            assertThat(playSuccessHaptic).isNotNull()
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun skipSuccessHaptic_onDeviceEntryFromSfps_whenPowerDown_sceneContainer() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = false)
+            underTest = kosmos.deviceEntryHapticsInteractor
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
+            // power button is currently DOWN
+            kosmos.fakeKeyEventRepository.setPowerButtonDown(true)
+
+            // It's been 10 seconds since the last power button wakeup
+            setAwakeFromPowerButton()
+            advanceTimeBy(10000)
+            runCurrent()
+
+            configureDeviceEntryFromBiometricSource(isFpUnlock = true)
+            verifyDeviceEntryFromFingerprintAuth()
+            assertThat(playSuccessHaptic).isNull()
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun skipSuccessHaptic_onDeviceEntryFromSfps_whenPowerButtonRecentlyPressed_sceneContainer() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = false)
+            underTest = kosmos.deviceEntryHapticsInteractor
+            val playSuccessHaptic by collectLastValue(underTest.playSuccessHaptic)
+            enrollFingerprint(FingerprintSensorType.POWER_BUTTON)
+            kosmos.fakeKeyEventRepository.setPowerButtonDown(false)
+
+            // It's only been 50ms since the last power button wakeup
+            setAwakeFromPowerButton()
+            advanceTimeBy(50)
+            runCurrent()
+
+            configureDeviceEntryFromBiometricSource(isFpUnlock = true)
+            verifyDeviceEntryFromFingerprintAuth()
+            assertThat(playSuccessHaptic).isNull()
+        }
+
+    // Mock dependencies for DeviceEntrySourceInteractor#deviceEntryFromBiometricSource
+    private fun configureDeviceEntryFromBiometricSource(
+        isFpUnlock: Boolean = false,
+        isFaceUnlock: Boolean = false,
+    ) {
+        // Mock DeviceEntrySourceInteractor#deviceEntryBiometricAuthSuccessState
+        if (isFpUnlock) {
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+        }
+        if (isFaceUnlock) {
+            kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus(
+                SuccessFaceAuthenticationStatus(
+                    FaceManager.AuthenticationResult(null, null, 0, true)
+                )
+            )
+
+            // Mock DeviceEntrySourceInteractor#faceWakeAndUnlockMode = MODE_UNLOCK_COLLAPSING
+            kosmos.sceneInteractor.setTransitionState(
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(Scenes.Lockscreen)
+                )
+            )
+        }
+        underTest = kosmos.deviceEntryHapticsInteractor
+    }
+
+    private fun TestScope.verifyDeviceEntryFromFingerprintAuth() {
+        val deviceEntryFromBiometricSource by
+            collectLastValue(kosmos.deviceEntrySourceInteractor.deviceEntryFromBiometricSource)
+        assertThat(deviceEntryFromBiometricSource)
+            .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+    }
+
+    private fun TestScope.verifyDeviceEntryFromFaceAuth() {
+        val deviceEntryFromBiometricSource by
+            collectLastValue(kosmos.deviceEntrySourceInteractor.deviceEntryFromBiometricSource)
+        assertThat(deviceEntryFromBiometricSource).isEqualTo(BiometricUnlockSource.FACE_SENSOR)
+    }
+
+    private fun enterDeviceFromFingerprintUnlockLegacy() {
         kosmos.fakeKeyguardRepository.setBiometricUnlockSource(
             BiometricUnlockSource.FINGERPRINT_SENSOR
         )
@@ -177,21 +355,22 @@
         )
     }
 
-    private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) {
-        kosmos.fingerprintPropertyRepository.setProperties(
-            sensorId = 0,
-            strength = SensorStrength.STRONG,
-            sensorType = fingerprintSensorType,
-            sensorLocations = mapOf(),
-        )
+    private fun enrollFingerprint(fingerprintSensorType: FingerprintSensorType?) {
+        if (fingerprintSensorType == null) {
+            kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+        } else {
+            kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+            kosmos.fingerprintPropertyRepository.setProperties(
+                sensorId = 0,
+                strength = SensorStrength.STRONG,
+                sensorType = fingerprintSensorType,
+                sensorLocations = mapOf(),
+            )
+        }
     }
 
-    private fun setPowerButtonFingerprintProperty() {
-        setFingerprintSensorType(FingerprintSensorType.POWER_BUTTON)
-    }
-
-    private fun setFingerprintEnrolled() {
-        kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+    private fun enrollFace() {
+        kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
     }
 
     private fun setAwakeFromPowerButton() {
@@ -202,9 +381,4 @@
             powerButtonLaunchGestureTriggered = false,
         )
     }
-
-    private fun coExEnrolledAndEnabled() {
-        setFingerprintEnrolled()
-        kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt
index 2e4c97b..b3c891d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorTest.kt
@@ -16,21 +16,51 @@
 
 package com.android.systemui.deviceentry.domain.interactor
 
+import android.hardware.face.FaceManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.keyguard.keyguardUpdateMonitor
 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.biometrics.authController
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.configureKeyguardBypass
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardBypassRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.verifyCallback
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.phone.dozeScrimController
+import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.statusbar.policy.devicePostureController
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -38,45 +68,328 @@
 class DeviceEntrySourceInteractorTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val keyguardRepository = kosmos.fakeKeyguardRepository
-    private val underTest = kosmos.deviceEntrySourceInteractor
+    private lateinit var underTest: DeviceEntrySourceInteractor
 
+    @Before
+    fun setup() {
+        if (SceneContainerFlag.isEnabled) {
+            whenever(kosmos.authController.isUdfpsFingerDown).thenReturn(false)
+            whenever(kosmos.dozeScrimController.isPulsing).thenReturn(false)
+            whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(true)
+            whenever(kosmos.screenOffAnimationController.isKeyguardShowDelayed()).thenReturn(false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+        } else {
+            underTest = kosmos.deviceEntrySourceInteractor
+        }
+    }
+
+    @DisableSceneContainer
     @Test
     fun deviceEntryFromFaceUnlock() =
         testScope.runTest {
             val deviceEntryFromBiometricAuthentication by
                 collectLastValue(underTest.deviceEntryFromBiometricSource)
-            keyguardRepository.setBiometricUnlockState(
+
+            kosmos.fakeKeyguardRepository.setBiometricUnlockState(
                 BiometricUnlockMode.WAKE_AND_UNLOCK,
                 BiometricUnlockSource.FACE_SENSOR,
             )
             runCurrent()
+
             assertThat(deviceEntryFromBiometricAuthentication)
                 .isEqualTo(BiometricUnlockSource.FACE_SENSOR)
         }
 
+    @DisableSceneContainer
     @Test
-    fun deviceEntryFromFingerprintUnlock() = runTest {
-        val deviceEntryFromBiometricAuthentication by
-            collectLastValue(underTest.deviceEntryFromBiometricSource)
-        keyguardRepository.setBiometricUnlockState(
-            BiometricUnlockMode.WAKE_AND_UNLOCK,
-            BiometricUnlockSource.FINGERPRINT_SENSOR,
-        )
-        runCurrent()
-        assertThat(deviceEntryFromBiometricAuthentication)
-            .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+    fun deviceEntryFromFingerprintUnlock() =
+        testScope.runTest {
+            val deviceEntryFromBiometricAuthentication by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            kosmos.fakeKeyguardRepository.setBiometricUnlockState(
+                BiometricUnlockMode.WAKE_AND_UNLOCK,
+                BiometricUnlockSource.FINGERPRINT_SENSOR,
+            )
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricAuthentication)
+                .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+        }
+
+    @DisableSceneContainer
+    @Test
+    fun noDeviceEntry() =
+        testScope.runTest {
+            val deviceEntryFromBiometricAuthentication by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            kosmos.fakeKeyguardRepository.setBiometricUnlockState(
+                BiometricUnlockMode.ONLY_WAKE, // doesn't dismiss keyguard:
+                BiometricUnlockSource.FINGERPRINT_SENSOR,
+            )
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricAuthentication).isNull()
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFingerprintUnlockOnLockScreen_sceneContainerEnabled() =
+        testScope.runTest {
+            underTest = kosmos.deviceEntrySourceInteractor
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true)
+            configureBiometricUnlockState(
+                alternateBouncerVisible = false,
+                sceneKey = Scenes.Lockscreen,
+            )
+            runCurrent()
+
+            kosmos.configureKeyguardBypass(isBypassAvailable = true)
+            underTest = kosmos.deviceEntrySourceInteractor
+
+            assertThat(deviceEntryFromBiometricSource)
+                .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFingerprintUnlockOnAod_sceneContainerEnabled() =
+        testScope.runTest {
+            underTest = kosmos.deviceEntrySourceInteractor
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(false)
+            configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true)
+            configureBiometricUnlockState(alternateBouncerVisible = false, sceneKey = Scenes.Dream)
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricSource)
+                .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFingerprintUnlockOnBouncer_sceneContainerEnabled() =
+        testScope.runTest {
+            underTest = kosmos.deviceEntrySourceInteractor
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true)
+            configureBiometricUnlockState(
+                alternateBouncerVisible = false,
+                sceneKey = Scenes.Bouncer,
+            )
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricSource)
+                .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFingerprintUnlockOnShade_sceneContainerEnabled() =
+        testScope.runTest {
+            underTest = kosmos.deviceEntrySourceInteractor
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true)
+            configureBiometricUnlockState(
+                alternateBouncerVisible = false,
+                sceneKey = Scenes.Lockscreen,
+            )
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricSource)
+                .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFingerprintUnlockOnAlternateBouncer_sceneContainerEnabled() =
+        testScope.runTest {
+            underTest = kosmos.deviceEntrySourceInteractor
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            configureDeviceEntryBiometricAuthSuccessState(isFingerprintAuth = true)
+            configureBiometricUnlockState(
+                alternateBouncerVisible = true,
+                sceneKey = Scenes.Lockscreen,
+            )
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricSource)
+                .isEqualTo(BiometricUnlockSource.FINGERPRINT_SENSOR)
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFaceUnlockOnLockScreen_bypassAvailable_sceneContainerEnabled() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = true)
+            underTest = kosmos.deviceEntrySourceInteractor
+
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true)
+            configureBiometricUnlockState(
+                alternateBouncerVisible = false,
+                sceneKey = Scenes.Lockscreen,
+            )
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricSource).isEqualTo(BiometricUnlockSource.FACE_SENSOR)
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFaceUnlockOnLockScreen_bypassDisabled_sceneContainerEnabled() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = false)
+            underTest = kosmos.deviceEntrySourceInteractor
+
+            collectLastValue(kosmos.keyguardBypassRepository.isBypassAvailable)
+            runCurrent()
+
+            val postureControllerCallback = kosmos.devicePostureController.verifyCallback()
+            postureControllerCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true)
+            configureBiometricUnlockState(
+                alternateBouncerVisible = false,
+                sceneKey = Scenes.Lockscreen,
+            )
+            runCurrent()
+
+            // MODE_NONE does not dismiss keyguard
+            assertThat(deviceEntryFromBiometricSource).isNull()
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFaceUnlockOnBouncer_sceneContainerEnabled() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = true)
+            underTest = kosmos.deviceEntrySourceInteractor
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true)
+            configureBiometricUnlockState(
+                alternateBouncerVisible = false,
+                sceneKey = Scenes.Bouncer,
+            )
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricSource).isEqualTo(BiometricUnlockSource.FACE_SENSOR)
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFaceUnlockOnShade_bypassAvailable_sceneContainerEnabled() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = true)
+            underTest = kosmos.deviceEntrySourceInteractor
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true)
+            configureBiometricUnlockState(alternateBouncerVisible = false, sceneKey = Scenes.Shade)
+            runCurrent()
+
+            // MODE_NONE does not dismiss keyguard
+            assertThat(deviceEntryFromBiometricSource).isNull()
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFaceUnlockOnShade_bypassDisabled_sceneContainerEnabled() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = false)
+            underTest = kosmos.deviceEntrySourceInteractor
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true)
+            configureBiometricUnlockState(
+                alternateBouncerVisible = false,
+                sceneKey = Scenes.Lockscreen,
+            )
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricSource).isNull()
+        }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntryFromFaceUnlockOnAlternateBouncer_sceneContainerEnabled() =
+        testScope.runTest {
+            kosmos.configureKeyguardBypass(isBypassAvailable = true)
+            underTest = kosmos.deviceEntrySourceInteractor
+            val deviceEntryFromBiometricSource by
+                collectLastValue(underTest.deviceEntryFromBiometricSource)
+
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
+            kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = true)
+            configureDeviceEntryBiometricAuthSuccessState(isFaceAuth = true)
+            configureBiometricUnlockState(
+                alternateBouncerVisible = true,
+                sceneKey = Scenes.Lockscreen,
+            )
+            runCurrent()
+
+            assertThat(deviceEntryFromBiometricSource).isEqualTo(BiometricUnlockSource.FACE_SENSOR)
+        }
+
+    private fun configureDeviceEntryBiometricAuthSuccessState(
+        isFingerprintAuth: Boolean = false,
+        isFaceAuth: Boolean = false,
+    ) {
+        if (isFingerprintAuth) {
+            val successStatus = SuccessFingerprintAuthenticationStatus(0, true)
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(successStatus)
+        }
+
+        if (isFaceAuth) {
+            val successStatus: FaceAuthenticationStatus =
+                SuccessFaceAuthenticationStatus(
+                    FaceManager.AuthenticationResult(null, null, 0, true)
+                )
+            kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus(successStatus)
+        }
     }
 
-    @Test
-    fun noDeviceEntry() = runTest {
-        val deviceEntryFromBiometricAuthentication by
-            collectLastValue(underTest.deviceEntryFromBiometricSource)
-        keyguardRepository.setBiometricUnlockState(
-            BiometricUnlockMode.ONLY_WAKE, // doesn't dismiss keyguard:
-            BiometricUnlockSource.FINGERPRINT_SENSOR,
+    private fun configureBiometricUnlockState(
+        alternateBouncerVisible: Boolean,
+        sceneKey: SceneKey,
+    ) {
+        kosmos.keyguardBouncerRepository.setAlternateVisible(alternateBouncerVisible)
+        kosmos.sceneInteractor.changeScene(sceneKey, "reason")
+        kosmos.sceneInteractor.setTransitionState(
+            MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(sceneKey))
         )
-        runCurrent()
-        assertThat(deviceEntryFromBiometricAuthentication).isNull()
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index 77337d3..a981e20 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Intent
 import android.content.mockedContext
+import android.content.res.Resources
 import android.hardware.fingerprint.FingerprintManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -41,13 +42,16 @@
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -55,6 +59,7 @@
 import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -63,8 +68,8 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val underTest = kosmos.occludingAppDeviceEntryInteractor
-
+    private lateinit var underTest: OccludingAppDeviceEntryInteractor
+    private lateinit var mockedResources: Resources
     private val fingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
     private val keyguardRepository = kosmos.fakeKeyguardRepository
     private val bouncerRepository = kosmos.keyguardBouncerRepository
@@ -74,9 +79,18 @@
     private val mockedContext = kosmos.mockedContext
     private val mockedActivityStarter = kosmos.activityStarter
 
+    @Before
+    fun setup() {
+        mockedResources = mock<Resources>()
+        whenever(mockedContext.resources).thenReturn(mockedResources)
+        whenever(mockedResources.getBoolean(R.bool.config_goToHomeFromOccludedApps))
+            .thenReturn(true)
+    }
+
     @Test
     fun fingerprintSuccess_goToHomeScreen() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             givenOnOccludingApp(true)
             fingerprintAuthRepository.setAuthenticationStatus(
                 SuccessFingerprintAuthenticationStatus(0, true)
@@ -86,8 +100,23 @@
         }
 
     @Test
+    fun fingerprintSuccess_configOff_doesNotGoToHomeScreen() =
+        testScope.runTest {
+            whenever(mockedResources.getBoolean(R.bool.config_goToHomeFromOccludedApps))
+                .thenReturn(false)
+            underTest = kosmos.occludingAppDeviceEntryInteractor
+            givenOnOccludingApp(true)
+            fingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+            verifyNeverGoToHomeScreen()
+        }
+
+    @Test
     fun fingerprintSuccess_notInteractive_doesNotGoToHomeScreen() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             givenOnOccludingApp(true)
             powerRepository.setInteractive(false)
             fingerprintAuthRepository.setAuthenticationStatus(
@@ -100,6 +129,7 @@
     @Test
     fun fingerprintSuccess_dreaming_doesNotGoToHomeScreen() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             givenOnOccludingApp(true)
             keyguardRepository.setDreaming(true)
             fingerprintAuthRepository.setAuthenticationStatus(
@@ -112,6 +142,7 @@
     @Test
     fun fingerprintSuccess_notOnOccludingApp_doesNotGoToHomeScreen() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             givenOnOccludingApp(false)
             fingerprintAuthRepository.setAuthenticationStatus(
                 SuccessFingerprintAuthenticationStatus(0, true)
@@ -123,11 +154,12 @@
     @Test
     fun lockout_goToHomeScreenOnDismissAction() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             givenOnOccludingApp(true)
             fingerprintAuthRepository.setAuthenticationStatus(
                 ErrorFingerprintAuthenticationStatus(
                     FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
-                    "lockoutTest"
+                    "lockoutTest",
                 )
             )
             runCurrent()
@@ -137,11 +169,12 @@
     @Test
     fun lockout_notOnOccludingApp_neverGoToHomeScreen() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             givenOnOccludingApp(false)
             fingerprintAuthRepository.setAuthenticationStatus(
                 ErrorFingerprintAuthenticationStatus(
                     FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
-                    "lockoutTest"
+                    "lockoutTest",
                 )
             )
             runCurrent()
@@ -151,11 +184,12 @@
     @Test
     fun lockout_onOccludingApp_onCommunal_neverGoToHomeScreen() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             givenOnOccludingApp(isOnOccludingApp = true, isOnCommunal = true)
             fingerprintAuthRepository.setAuthenticationStatus(
                 ErrorFingerprintAuthenticationStatus(
                     FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
-                    "lockoutTest"
+                    "lockoutTest",
                 )
             )
             runCurrent()
@@ -165,6 +199,7 @@
     @Test
     fun message_fpFailOnOccludingApp_thenNotOnOccludingApp() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             val message by collectLastValue(underTest.message)
 
             givenOnOccludingApp(true)
@@ -186,6 +221,7 @@
     @Test
     fun message_fpErrorHelpFailOnOccludingApp() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             val message by collectLastValue(underTest.message)
 
             givenOnOccludingApp(true)
@@ -218,6 +254,7 @@
     @Test
     fun message_fpError_lockoutFilteredOut() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             val message by collectLastValue(underTest.message)
 
             givenOnOccludingApp(true)
@@ -246,6 +283,7 @@
     @Test
     fun noMessage_fpErrorsWhileDozing() =
         testScope.runTest {
+            underTest = kosmos.occludingAppDeviceEntryInteractor
             val message by collectLastValue(underTest.message)
 
             givenOnOccludingApp(true)
@@ -254,7 +292,7 @@
             kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.OCCLUDED,
                 to = KeyguardState.DOZING,
-                testScope
+                testScope,
             )
             runCurrent()
 
@@ -283,7 +321,7 @@
 
     private suspend fun givenOnOccludingApp(
         isOnOccludingApp: Boolean,
-        isOnCommunal: Boolean = false
+        isOnCommunal: Boolean = false,
     ) {
         powerRepository.setInteractive(true)
         keyguardRepository.setIsDozing(false)
@@ -305,13 +343,13 @@
             kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
                 to = KeyguardState.OCCLUDED,
-                testScope
+                testScope,
             )
         } else {
             kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.OCCLUDED,
                 to = KeyguardState.LOCKSCREEN,
-                testScope
+                testScope,
             )
         }
     }
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 90%
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 5a76489..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,6 +19,7 @@
 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
@@ -49,14 +50,18 @@
 
     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() {
@@ -81,6 +86,7 @@
                         windowType = WINDOW_TYPE_FOO,
                         context = applicationContext,
                         windowManager = applicationWindowManager,
+                        layoutInflater = applicationLayoutInflater,
                     )
                 )
         }
@@ -102,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/dreams/homecontrols/HomeControlsDreamServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt
index 9300db9..4317b9f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt
@@ -16,25 +16,37 @@
 package com.android.systemui.dreams.homecontrols
 
 import android.app.Activity
+import android.content.ComponentName
 import android.content.Intent
+import android.os.powerManager
 import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_ACTIVITY_PANEL
 import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_DREAM
 import android.service.controls.ControlsProviderService.EXTRA_CONTROLS_SURFACE
+import android.service.dreams.DreamService
 import android.window.TaskFragmentInfo
+import androidx.lifecycle.testing.TestLifecycleOwner
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.controls.settings.FakeControlsSettingsRepository
+import com.android.systemui.dreams.homecontrols.service.TaskFragmentComponent
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
+import com.android.systemui.dreams.homecontrols.shared.model.fakeHomeControlsDataSource
+import com.android.systemui.dreams.homecontrols.shared.model.homeControlsDataSource
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.testKosmos
+import com.android.systemui.util.time.fakeSystemClock
 import com.android.systemui.util.wakelock.WakeLockFake
 import com.google.common.truth.Truth.assertThat
-import java.util.Optional
+import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runCurrent
 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
@@ -47,7 +59,6 @@
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -62,13 +73,18 @@
         WakeLockFake.Builder(context).apply { setWakeLock(fakeWakeLock) }
     }
 
+    private val lifecycleOwner = TestLifecycleOwner(coroutineDispatcher = kosmos.testDispatcher)
+
     private val taskFragmentComponent = mock<TaskFragmentComponent>()
     private val activity = mock<Activity>()
     private val onCreateCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>()
     private val onInfoChangedCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>()
     private val hideCallback = argumentCaptor<() -> Unit>()
-    private val dreamServiceDelegate =
-        mock<DreamServiceDelegate> { on { getActivity(any()) } doReturn activity }
+    private var dreamService =
+        mock<DreamService> {
+            on { activity } doReturn activity
+            on { redirectWake } doReturn false
+        }
 
     private val taskFragmentComponentFactory =
         mock<TaskFragmentComponent.Factory> {
@@ -82,12 +98,32 @@
             } doReturn taskFragmentComponent
         }
 
-    private val underTest: HomeControlsDreamService by lazy { buildService() }
+    private val underTest: HomeControlsDreamServiceImpl by lazy {
+        with(kosmos) {
+            HomeControlsDreamServiceImpl(
+                taskFragmentFactory = taskFragmentComponentFactory,
+                wakeLockBuilder = fakeWakeLockBuilder,
+                powerManager = powerManager,
+                systemClock = fakeSystemClock,
+                dataSource = homeControlsDataSource,
+                logBuffer = logcatLogBuffer("HomeControlsDreamServiceTest"),
+                service = dreamService,
+                lifecycleOwner = lifecycleOwner,
+            )
+        }
+    }
 
     @Before
     fun setup() {
-        whenever(kosmos.controlsComponent.getControlsListingController())
-            .thenReturn(Optional.of(kosmos.controlsListingController))
+        Dispatchers.setMain(kosmos.testDispatcher)
+        kosmos.fakeHomeControlsDataSource.setComponentInfo(
+            HomeControlsComponentInfo(PANEL_COMPONENT, true)
+        )
+    }
+
+    @After
+    fun tearDown() {
+        Dispatchers.resetMain()
     }
 
     @Test
@@ -108,13 +144,10 @@
     @Test
     fun testNotCreatingTaskFragmentComponentWhenActivityIsNull() =
         testScope.runTest {
-            val serviceWithNullActivity =
-                buildService(
-                    mock<DreamServiceDelegate> { on { getActivity(underTest) } doReturn null }
-                )
-
-            serviceWithNullActivity.onAttachedToWindow()
+            dreamService = mock<DreamService> { on { activity } doReturn null }
+            underTest.onAttachedToWindow()
             verify(taskFragmentComponentFactory, never()).create(any(), any(), any(), any())
+            verify(dreamService).finish()
         }
 
     @Test
@@ -137,9 +170,9 @@
     @Test
     fun testFinishesDreamWithoutRestartingActivityWhenNotRedirectingWakes() =
         testScope.runTest {
-            whenever(dreamServiceDelegate.redirectWake(any())).thenReturn(false)
             underTest.onAttachedToWindow()
             onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>())
+            runCurrent()
             verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher())
 
             // Task fragment becomes empty
@@ -149,16 +182,21 @@
             advanceUntilIdle()
             // Dream is finished and activity is not restarted
             verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher())
-            verify(dreamServiceDelegate, never()).wakeUp(any())
-            verify(dreamServiceDelegate).finish(any())
+            verify(dreamService, never()).wakeUp()
+            verify(dreamService).finish()
         }
 
     @Test
     fun testRestartsActivityWhenRedirectingWakes() =
         testScope.runTest {
-            whenever(dreamServiceDelegate.redirectWake(any())).thenReturn(true)
+            dreamService =
+                mock<DreamService> {
+                    on { activity } doReturn activity
+                    on { redirectWake } doReturn true
+                }
             underTest.onAttachedToWindow()
             onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>())
+            runCurrent()
             verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher())
 
             // Task fragment becomes empty
@@ -166,30 +204,20 @@
                 mock<TaskFragmentInfo> { on { isEmpty } doReturn true }
             )
             advanceUntilIdle()
+
             // Activity is restarted instead of finishing the dream.
             verify(taskFragmentComponent, times(2)).startActivityInTaskFragment(intentMatcher())
-            verify(dreamServiceDelegate).wakeUp(any())
-            verify(dreamServiceDelegate, never()).finish(any())
+            verify(dreamService).wakeUp()
+            verify(dreamService, never()).finish()
         }
 
     private fun intentMatcher() =
         argThat<Intent> {
             getIntExtra(EXTRA_CONTROLS_SURFACE, CONTROLS_SURFACE_ACTIVITY_PANEL) ==
-                CONTROLS_SURFACE_DREAM
+                CONTROLS_SURFACE_DREAM && component == PANEL_COMPONENT
         }
 
-    private fun buildService(
-        activityProvider: DreamServiceDelegate = dreamServiceDelegate
-    ): HomeControlsDreamService =
-        with(kosmos) {
-            return HomeControlsDreamService(
-                controlsSettingsRepository = FakeControlsSettingsRepository(),
-                taskFragmentFactory = taskFragmentComponentFactory,
-                homeControlsComponentInteractor = homeControlsComponentInteractor,
-                wakeLockBuilder = fakeWakeLockBuilder,
-                dreamServiceDelegate = activityProvider,
-                bgDispatcher = testDispatcher,
-                logBuffer = logcatLogBuffer("HomeControlsDreamServiceTest")
-            )
-        }
+    private companion object {
+        val PANEL_COMPONENT = ComponentName("test.pkg", "test.panel")
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartableTest.kt
index 1adf414..ef02817 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartableTest.kt
@@ -23,82 +23,86 @@
 import android.content.pm.UserInfo
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_HOME_CONTROLS_DREAM_HSUM
+import com.android.systemui.Flags.homeControlsDreamHsum
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlsServiceInfo
-import com.android.systemui.controls.dagger.ControlsComponent
-import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.controls.panels.SelectedComponentRepository
 import com.android.systemui.controls.panels.authorizedPanelsRepository
 import com.android.systemui.controls.panels.selectedComponentRepository
-import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
+import com.android.systemui.dreams.homecontrols.system.HomeControlsDreamStartable
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsComponent
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsListingController
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.homeControlsComponentInteractor
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.settings.userTracker
 import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
 import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class HomeControlsDreamStartableTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class HomeControlsDreamStartableTest(flags: FlagsParameterization) : SysuiTestCase() {
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
 
     private val kosmos = testKosmos()
-
-    @Mock private lateinit var packageManager: PackageManager
-
-    private lateinit var homeControlsComponentInteractor: HomeControlsComponentInteractor
-    private lateinit var selectedComponentRepository: SelectedComponentRepository
-    private lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository
-    private lateinit var userRepository: FakeUserRepository
-    private lateinit var controlsComponent: ControlsComponent
-    private lateinit var controlsListingController: ControlsListingController
-
-    private lateinit var startable: HomeControlsDreamStartable
-    private val componentName = ComponentName(context, HomeControlsDreamService::class.java)
     private val testScope = kosmos.testScope
 
+    private val systemUserPackageManager = mock<PackageManager>()
+    private val userPackageManager = mock<PackageManager>()
+
+    private val selectedComponentRepository = kosmos.selectedComponentRepository
+    private val userRepository =
+        kosmos.fakeUserRepository.apply { setUserInfos(listOf(PRIMARY_USER)) }
+    private val controlsListingController =
+        kosmos.controlsListingController.stub {
+            on { getCurrentServices() } doReturn
+                listOf(buildControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true))
+        }
+    private val controlsComponent =
+        kosmos.controlsComponent.stub {
+            on { getControlsListingController() } doReturn Optional.of(controlsListingController)
+        }
+
+    private val underTest by lazy {
+        HomeControlsDreamStartable(
+            mContext,
+            systemUserPackageManager,
+            kosmos.userTracker,
+            kosmos.homeControlsComponentInteractor,
+            kosmos.applicationCoroutineScope,
+        )
+    }
+    private val componentName = ComponentName(context, HomeControlsDreamService::class.java)
+
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        selectedComponentRepository = kosmos.selectedComponentRepository
-        authorizedPanelsRepository = kosmos.authorizedPanelsRepository
-        userRepository = kosmos.fakeUserRepository
-        controlsComponent = kosmos.controlsComponent
-        controlsListingController = kosmos.controlsListingController
-
-        userRepository.setUserInfos(listOf(PRIMARY_USER))
-
-        authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE_PANEL))
-
+        kosmos.authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE_PANEL))
         whenever(controlsComponent.getControlsListingController())
             .thenReturn(Optional.of(controlsListingController))
-        whenever(controlsListingController.getCurrentServices())
-            .thenReturn(listOf(ControlsServiceInfo(TEST_COMPONENT_PANEL, "panel", hasPanel = true)))
-
-        homeControlsComponentInteractor = kosmos.homeControlsComponentInteractor
-
-        startable =
-            HomeControlsDreamStartable(
-                mContext,
-                packageManager,
-                homeControlsComponentInteractor,
-                kosmos.applicationCoroutineScope
-            )
+        whenever(kosmos.fakeUserTracker.userContext.packageManager).thenReturn(userPackageManager)
     }
 
     @Test
@@ -107,13 +111,19 @@
         testScope.runTest {
             userRepository.setSelectedUserInfo(PRIMARY_USER)
             selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
-            startable.start()
+            underTest.start()
             runCurrent()
+            val packageManager =
+                if (homeControlsDreamHsum()) {
+                    userPackageManager
+                } else {
+                    systemUserPackageManager
+                }
             verify(packageManager)
                 .setComponentEnabledSetting(
-                    eq(componentName),
-                    eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
-                    eq(PackageManager.DONT_KILL_APP)
+                    componentName,
+                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                    PackageManager.DONT_KILL_APP,
                 )
         }
 
@@ -122,13 +132,19 @@
     fun testStartDisablesHomeControlsDreamServiceWhenPanelComponentIsNull() =
         testScope.runTest {
             selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_NON_PANEL)
-            startable.start()
+            underTest.start()
             runCurrent()
+            val packageManager =
+                if (homeControlsDreamHsum()) {
+                    userPackageManager
+                } else {
+                    systemUserPackageManager
+                }
             verify(packageManager)
                 .setComponentEnabledSetting(
-                    eq(componentName),
-                    eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
-                    eq(PackageManager.DONT_KILL_APP)
+                    componentName,
+                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    PackageManager.DONT_KILL_APP,
                 )
         }
 
@@ -137,20 +153,26 @@
     fun testStartDisablesDreamServiceWhenFlagIsDisabled() =
         testScope.runTest {
             selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_NON_PANEL)
-            startable.start()
+            underTest.start()
             runCurrent()
+            val packageManager =
+                if (homeControlsDreamHsum()) {
+                    userPackageManager
+                } else {
+                    systemUserPackageManager
+                }
             verify(packageManager)
                 .setComponentEnabledSetting(
                     eq(componentName),
                     eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
-                    eq(PackageManager.DONT_KILL_APP)
+                    eq(PackageManager.DONT_KILL_APP),
                 )
         }
 
-    private fun ControlsServiceInfo(
+    private fun buildControlsServiceInfo(
         componentName: ComponentName,
         label: CharSequence,
-        hasPanel: Boolean
+        hasPanel: Boolean,
     ): ControlsServiceInfo {
         val serviceInfo =
             ServiceInfo().apply {
@@ -165,7 +187,7 @@
         context: Context,
         serviceInfo: ServiceInfo,
         private val label: CharSequence,
-        hasPanel: Boolean
+        hasPanel: Boolean,
     ) : ControlsServiceInfo(context, serviceInfo) {
 
         init {
@@ -180,12 +202,16 @@
     }
 
     companion object {
+        @get:Parameters(name = "{0}")
+        @JvmStatic
+        val params = FlagsParameterization.allCombinationsOf(FLAG_HOME_CONTROLS_DREAM_HSUM)
+
         private const val PRIMARY_USER_ID = 0
         private val PRIMARY_USER =
             UserInfo(
                 /* id= */ PRIMARY_USER_ID,
                 /* name= */ "primary user",
-                /* flags= */ UserInfo.FLAG_PRIMARY
+                /* flags= */ UserInfo.FLAG_PRIMARY,
             )
         private const val TEST_PACKAGE_PANEL = "pkg.panel"
         private val TEST_COMPONENT_PANEL = ComponentName(TEST_PACKAGE_PANEL, "service")
@@ -193,13 +219,13 @@
             SelectedComponentRepository.SelectedComponent(
                 TEST_PACKAGE_PANEL,
                 TEST_COMPONENT_PANEL,
-                true
+                true,
             )
         private val TEST_SELECTED_COMPONENT_NON_PANEL =
             SelectedComponentRepository.SelectedComponent(
                 TEST_PACKAGE_PANEL,
                 TEST_COMPONENT_PANEL,
-                false
+                false,
             )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyTest.kt
new file mode 100644
index 0000000..e57776f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyTest.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.dreams.homecontrols.service
+
+import android.content.ComponentName
+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.dreams.homecontrols.shared.model.HomeControlsComponentInfo
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+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)
+class HomeControlsRemoteProxyTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val fakeBinder = kosmos.fakeHomeControlsRemoteBinder
+
+    private val underTest by lazy { kosmos.homeControlsRemoteProxy }
+
+    @Test
+    fun testRegistersOnlyWhileSubscribed() =
+        testScope.runTest {
+            assertThat(fakeBinder.callbacks).isEmpty()
+
+            val job = launch { underTest.componentInfo.collect {} }
+            runCurrent()
+            assertThat(fakeBinder.callbacks).hasSize(1)
+
+            job.cancel()
+            runCurrent()
+            assertThat(fakeBinder.callbacks).isEmpty()
+        }
+
+    @Test
+    fun testEmitsOnCallback() =
+        testScope.runTest {
+            val componentInfo by collectLastValue(underTest.componentInfo)
+            assertThat(componentInfo).isNull()
+
+            fakeBinder.notifyCallbacks(TEST_COMPONENT, allowTrivialControlsOnLockscreen = true)
+            assertThat(componentInfo)
+                .isEqualTo(
+                    HomeControlsComponentInfo(
+                        TEST_COMPONENT,
+                        allowTrivialControlsOnLockscreen = true,
+                    )
+                )
+        }
+
+    @Test
+    fun testOnlyRegistersSingleCallbackForMultipleSubscribers() =
+        testScope.runTest {
+            assertThat(fakeBinder.callbacks).isEmpty()
+
+            // 2 collectors
+            val job = launch {
+                launch { underTest.componentInfo.collect {} }
+                launch { underTest.componentInfo.collect {} }
+            }
+            runCurrent()
+            assertThat(fakeBinder.callbacks).hasSize(1)
+            job.cancel()
+        }
+
+    private companion object {
+        val TEST_COMPONENT = ComponentName("pkg.test", "class.test")
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorTest.kt
new file mode 100644
index 0000000..4002175
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorTest.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.dreams.homecontrols.service
+
+import android.content.ComponentName
+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.dreams.homecontrols.dagger.HomeControlsRemoteServiceComponent
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.service.ObservableServiceConnection
+import com.android.systemui.util.service.PersistentConnectionManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RemoteHomeControlsDataSourceDelegatorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val proxy = kosmos.homeControlsRemoteProxy
+    private val fakeBinder = kosmos.fakeHomeControlsRemoteBinder
+
+    private val callbackCaptor =
+        argumentCaptor<ObservableServiceConnection.Callback<HomeControlsRemoteProxy>>()
+
+    private val connectionManager =
+        mock<PersistentConnectionManager<HomeControlsRemoteProxy>> {
+            on { start() } doAnswer { simulateConnect() }
+            on { stop() } doAnswer { simulateDisconnect() }
+        }
+    private val serviceComponent =
+        mock<HomeControlsRemoteServiceComponent> {
+            on { connectionManager } doReturn connectionManager
+        }
+
+    private val underTest by lazy { kosmos.remoteHomeControlsDataSourceDelegator }
+
+    @Before
+    fun setUp() {
+        kosmos.homeControlsRemoteServiceFactory =
+            mock<HomeControlsRemoteServiceComponent.Factory>().stub {
+                on { create(callbackCaptor.capture()) } doReturn serviceComponent
+            }
+    }
+
+    @Test
+    fun testQueriesComponentInfoFromBinder() =
+        testScope.runTest {
+            assertThat(fakeBinder.callbacks).isEmpty()
+
+            val componentInfo by collectLastValue(underTest.componentInfo)
+
+            assertThat(componentInfo).isNull()
+            assertThat(fakeBinder.callbacks).hasSize(1)
+
+            fakeBinder.notifyCallbacks(TEST_COMPONENT, allowTrivialControlsOnLockscreen = true)
+            assertThat(componentInfo)
+                .isEqualTo(
+                    HomeControlsComponentInfo(
+                        TEST_COMPONENT,
+                        allowTrivialControlsOnLockscreen = true,
+                    )
+                )
+        }
+
+    @Test
+    fun testOnlyConnectToServiceOnSubscription() =
+        testScope.runTest {
+            verify(connectionManager, never()).start()
+
+            val job = launch { underTest.componentInfo.collect {} }
+            runCurrent()
+            verify(connectionManager, times(1)).start()
+            verify(connectionManager, never()).stop()
+
+            job.cancel()
+            runCurrent()
+            verify(connectionManager, times(1)).start()
+            verify(connectionManager, times(1)).stop()
+        }
+
+    private fun simulateConnect() {
+        callbackCaptor.lastValue.onConnected(mock(), proxy)
+    }
+
+    private fun simulateDisconnect() {
+        callbackCaptor.lastValue.onDisconnected(mock(), 0)
+    }
+
+    private companion object {
+        val TEST_COMPONENT = ComponentName("pkg.test", "class.test")
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteServiceBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteServiceBinderTest.kt
new file mode 100644
index 0000000..f8a45e8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteServiceBinderTest.kt
@@ -0,0 +1,236 @@
+/*
+ * 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.dreams.homecontrols.system
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.ServiceInfo
+import android.content.pm.UserInfo
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.panels.SelectedComponentRepository
+import com.android.systemui.controls.panels.authorizedPanelsRepository
+import com.android.systemui.controls.panels.selectedComponentRepository
+import com.android.systemui.controls.settings.FakeControlsSettingsRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsComponent
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsListingController
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.homeControlsComponentInteractor
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+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
+import org.mockito.Mockito
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HomeControlsRemoteServiceBinderTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val lifecycleOwner = TestLifecycleOwner(coroutineDispatcher = kosmos.testDispatcher)
+    private val fakeControlsSettingsRepository = FakeControlsSettingsRepository()
+
+    private val underTest by lazy {
+        HomeControlsRemoteServiceBinder(
+            kosmos.homeControlsComponentInteractor,
+            fakeControlsSettingsRepository,
+            kosmos.backgroundCoroutineContext,
+            logcatLogBuffer(),
+            lifecycleOwner,
+        )
+    }
+
+    @Before
+    fun setUp() {
+        with(kosmos) {
+            fakeUserRepository.setUserInfos(listOf(PRIMARY_USER))
+            whenever(controlsComponent.getControlsListingController())
+                .thenReturn(Optional.of(controlsListingController))
+        }
+    }
+
+    @Test
+    fun testRegisterSingleListener() =
+        testScope.runTest {
+            setup()
+            val controlsSettings by collectLastValue(addCallback())
+            runServicesUpdate()
+
+            assertThat(controlsSettings)
+                .isEqualTo(
+                    CallbackArgs(
+                        panelComponent = TEST_COMPONENT,
+                        allowTrivialControlsOnLockscreen = false,
+                    )
+                )
+        }
+
+    @Test
+    fun testRegisterMultipleListeners() =
+        testScope.runTest {
+            setup()
+            val controlsSettings1 by collectLastValue(addCallback())
+            val controlsSettings2 by collectLastValue(addCallback())
+            runServicesUpdate()
+
+            assertThat(controlsSettings1)
+                .isEqualTo(
+                    CallbackArgs(
+                        panelComponent = TEST_COMPONENT,
+                        allowTrivialControlsOnLockscreen = false,
+                    )
+                )
+            assertThat(controlsSettings2)
+                .isEqualTo(
+                    CallbackArgs(
+                        panelComponent = TEST_COMPONENT,
+                        allowTrivialControlsOnLockscreen = false,
+                    )
+                )
+        }
+
+    @Test
+    fun testListenerCalledWhenStateChanges() =
+        testScope.runTest {
+            setup()
+            val controlsSettings by collectLastValue(addCallback())
+            runServicesUpdate()
+
+            assertThat(controlsSettings)
+                .isEqualTo(
+                    CallbackArgs(
+                        panelComponent = TEST_COMPONENT,
+                        allowTrivialControlsOnLockscreen = false,
+                    )
+                )
+
+            kosmos.authorizedPanelsRepository.removeAuthorizedPanels(setOf(TEST_PACKAGE))
+
+            // Updated with null component now that we are no longer authorized.
+            assertThat(controlsSettings)
+                .isEqualTo(
+                    CallbackArgs(panelComponent = null, allowTrivialControlsOnLockscreen = false)
+                )
+        }
+
+    private fun TestScope.runServicesUpdate() {
+        runCurrent()
+        val listings = listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
+        val callback = withArgCaptor {
+            Mockito.verify(kosmos.controlsListingController).addCallback(capture())
+        }
+        callback.onServicesUpdated(listings)
+        runCurrent()
+    }
+
+    private fun addCallback() = conflatedCallbackFlow {
+        val callback =
+            object : IOnControlsSettingsChangeListener.Stub() {
+                override fun onControlsSettingsChanged(
+                    panelComponent: ComponentName?,
+                    allowTrivialControlsOnLockscreen: Boolean,
+                ) {
+                    trySend(CallbackArgs(panelComponent, allowTrivialControlsOnLockscreen))
+                }
+            }
+        underTest.registerListenerForCurrentUser(callback)
+        awaitClose { underTest.unregisterListenerForCurrentUser(callback) }
+    }
+
+    private suspend fun TestScope.setup() {
+        kosmos.fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+        kosmos.fakeUserTracker.set(listOf(PRIMARY_USER), 0)
+        kosmos.authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
+        kosmos.selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
+        runCurrent()
+    }
+
+    private data class CallbackArgs(
+        val panelComponent: ComponentName?,
+        val allowTrivialControlsOnLockscreen: Boolean,
+    )
+
+    private fun ControlsServiceInfo(
+        componentName: ComponentName,
+        label: CharSequence,
+        hasPanel: Boolean,
+    ): ControlsServiceInfo {
+        val serviceInfo =
+            ServiceInfo().apply {
+                applicationInfo = ApplicationInfo()
+                packageName = componentName.packageName
+                name = componentName.className
+            }
+        return FakeControlsServiceInfo(context, serviceInfo, label, hasPanel)
+    }
+
+    private class FakeControlsServiceInfo(
+        context: Context,
+        serviceInfo: ServiceInfo,
+        private val label: CharSequence,
+        hasPanel: Boolean,
+    ) : ControlsServiceInfo(context, serviceInfo) {
+
+        init {
+            if (hasPanel) {
+                panelActivity = serviceInfo.componentName
+            }
+        }
+
+        override fun loadLabel(): CharSequence {
+            return label
+        }
+    }
+
+    private companion object {
+        const val PRIMARY_USER_ID = 0
+        val PRIMARY_USER =
+            UserInfo(
+                /* id= */ PRIMARY_USER_ID,
+                /* name= */ "primary user",
+                /* flags= */ UserInfo.FLAG_PRIMARY,
+            )
+
+        private const val TEST_PACKAGE = "pkg"
+        private val TEST_COMPONENT = ComponentName(TEST_PACKAGE, "service")
+        private val TEST_SELECTED_COMPONENT_PANEL =
+            SelectedComponentRepository.SelectedComponent(TEST_PACKAGE, TEST_COMPONENT, true)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorTest.kt
similarity index 64%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorTest.kt
index 7292985..c950523 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorTest.kt
@@ -20,40 +20,32 @@
 import android.content.pm.ApplicationInfo
 import android.content.pm.ServiceInfo
 import android.content.pm.UserInfo
-import android.os.PowerManager
-import android.os.UserHandle
-import android.os.powerManager
-import android.service.dream.dreamManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.data.repository.fakePackageChangeRepository
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.panels.SelectedComponentRepository
 import com.android.systemui.controls.panels.authorizedPanelsRepository
 import com.android.systemui.controls.panels.selectedComponentRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor.Companion.MAX_UPDATE_CORRELATION_DELAY
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsComponent
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.controlsListingController
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.homeControlsComponentInteractor
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.mockito.withArgCaptor
-import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
 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
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mockito.anyLong
-import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -68,7 +60,6 @@
     @Before
     fun setUp() =
         with(kosmos) {
-            fakeSystemClock.setCurrentTimeMillis(0)
             fakeUserRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER))
             whenever(controlsComponent.getControlsListingController())
                 .thenReturn(Optional.of(controlsListingController))
@@ -172,113 +163,6 @@
             }
         }
 
-    @Test
-    fun testMonitoringUpdatesAndRestart() =
-        with(kosmos) {
-            testScope.runTest {
-                setActiveUser(PRIMARY_USER)
-                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
-                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
-                whenever(controlsListingController.getCurrentServices())
-                    .thenReturn(
-                        listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
-                    )
-
-                val job = launch { underTest.monitorUpdatesAndRestart() }
-                val panelComponent by collectLastValue(underTest.panelComponent)
-
-                assertThat(panelComponent).isEqualTo(TEST_COMPONENT)
-                verify(dreamManager, never()).startDream()
-
-                fakeSystemClock.advanceTime(100)
-                // The package update is started.
-                fakePackageChangeRepository.notifyUpdateStarted(
-                    TEST_PACKAGE,
-                    UserHandle.of(PRIMARY_USER_ID),
-                )
-                fakeSystemClock.advanceTime(MAX_UPDATE_CORRELATION_DELAY.inWholeMilliseconds)
-                // Task fragment becomes empty as a result of the update.
-                underTest.onDreamEndUnexpectedly()
-
-                runCurrent()
-                verify(dreamManager, never()).startDream()
-
-                fakeSystemClock.advanceTime(500)
-                // The package update is finished.
-                fakePackageChangeRepository.notifyUpdateFinished(
-                    TEST_PACKAGE,
-                    UserHandle.of(PRIMARY_USER_ID),
-                )
-
-                runCurrent()
-                verify(dreamManager).startDream()
-                job.cancel()
-            }
-        }
-
-    @Test
-    fun testMonitoringUpdatesAndRestart_dreamEndsAfterDelay() =
-        with(kosmos) {
-            testScope.runTest {
-                setActiveUser(PRIMARY_USER)
-                authorizedPanelsRepository.addAuthorizedPanels(setOf(TEST_PACKAGE))
-                selectedComponentRepository.setSelectedComponent(TEST_SELECTED_COMPONENT_PANEL)
-                whenever(controlsListingController.getCurrentServices())
-                    .thenReturn(
-                        listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = true))
-                    )
-
-                val job = launch { underTest.monitorUpdatesAndRestart() }
-                val panelComponent by collectLastValue(underTest.panelComponent)
-
-                assertThat(panelComponent).isEqualTo(TEST_COMPONENT)
-                verify(dreamManager, never()).startDream()
-
-                fakeSystemClock.advanceTime(100)
-                // The package update is started.
-                fakePackageChangeRepository.notifyUpdateStarted(
-                    TEST_PACKAGE,
-                    UserHandle.of(PRIMARY_USER_ID),
-                )
-                fakeSystemClock.advanceTime(MAX_UPDATE_CORRELATION_DELAY.inWholeMilliseconds + 100)
-                // Task fragment becomes empty as a result of the update.
-                underTest.onDreamEndUnexpectedly()
-
-                runCurrent()
-                verify(dreamManager, never()).startDream()
-
-                fakeSystemClock.advanceTime(500)
-                // The package update is finished.
-                fakePackageChangeRepository.notifyUpdateFinished(
-                    TEST_PACKAGE,
-                    UserHandle.of(PRIMARY_USER_ID),
-                )
-
-                runCurrent()
-                verify(dreamManager, never()).startDream()
-                job.cancel()
-            }
-        }
-
-    @Test
-    fun testDreamUnexpectedlyEnds_triggersUserActivity() =
-        with(kosmos) {
-            testScope.runTest {
-                fakeSystemClock.setUptimeMillis(100000L)
-                verify(powerManager, never()).userActivity(anyLong(), anyInt(), anyInt())
-
-                // Dream ends unexpectedly
-                underTest.onDreamEndUnexpectedly()
-
-                verify(powerManager)
-                    .userActivity(
-                        100000L,
-                        PowerManager.USER_ACTIVITY_EVENT_OTHER,
-                        PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS
-                    )
-            }
-        }
-
     private fun runServicesUpdate(hasPanelBoolean: Boolean = true) {
         val listings =
             listOf(ControlsServiceInfo(TEST_COMPONENT, "panel", hasPanel = hasPanelBoolean))
@@ -297,7 +181,7 @@
     private fun ControlsServiceInfo(
         componentName: ComponentName,
         label: CharSequence,
-        hasPanel: Boolean
+        hasPanel: Boolean,
     ): ControlsServiceInfo {
         val serviceInfo =
             ServiceInfo().apply {
@@ -312,7 +196,7 @@
         context: Context,
         serviceInfo: ServiceInfo,
         private val label: CharSequence,
-        hasPanel: Boolean
+        hasPanel: Boolean,
     ) : ControlsServiceInfo(context, serviceInfo) {
 
         init {
@@ -332,7 +216,7 @@
             UserInfo(
                 /* id= */ PRIMARY_USER_ID,
                 /* name= */ "primary user",
-                /* flags= */ UserInfo.FLAG_PRIMARY
+                /* flags= */ UserInfo.FLAG_PRIMARY,
             )
 
         private const val ANOTHER_USER_ID = 1
@@ -340,7 +224,7 @@
             UserInfo(
                 /* id= */ ANOTHER_USER_ID,
                 /* name= */ "another user",
-                /* flags= */ UserInfo.FLAG_PRIMARY
+                /* flags= */ UserInfo.FLAG_PRIMARY,
             )
         private const val TEST_PACKAGE = "pkg"
         private val TEST_COMPONENT = ComponentName(TEST_PACKAGE, "service")
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/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
index 9e20e7d..620b8b6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
@@ -16,7 +16,11 @@
 
 package com.android.systemui.keyboard.shortcut.data.repository
 
+import android.graphics.drawable.Drawable
+import android.hardware.input.KeyGlyphMap
 import android.hardware.input.fakeInputManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.view.KeyEvent.KEYCODE_1
 import android.view.KeyEvent.KEYCODE_A
 import android.view.KeyEvent.KEYCODE_B
@@ -26,10 +30,12 @@
 import android.view.KeyEvent.KEYCODE_F
 import android.view.KeyEvent.KEYCODE_G
 import android.view.KeyEvent.META_FUNCTION_ON
+import android.view.KeyEvent.META_META_ON
 import android.view.KeyboardShortcutGroup
 import android.view.KeyboardShortcutInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_SHORTCUT_HELPER_KEY_GLYPH
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
@@ -49,6 +55,7 @@
 import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -57,6 +64,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -187,6 +197,79 @@
                 )
         }
 
+    @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+    @Test
+    fun modifierMappedToCustomDrawableWhenKeyGlyphMapExists() =
+        testScope.runTest {
+            val metaDrawable = mock(Drawable::class.java)
+            val keyGlyph = mock(KeyGlyphMap::class.java)
+            whenever(keyGlyph.getDrawableForModifierState(context, META_META_ON))
+                .thenReturn(metaDrawable)
+            whenever(kosmos.fakeInputManager.inputManager.getKeyGlyphMap(anyInt()))
+                .thenReturn(keyGlyph)
+            fakeSystemSource.setGroups(simpleGroup(simpleShortcutInfo(KEYCODE_1, META_META_ON)))
+            helper.toggle(deviceId = 123)
+
+            val categories by collectLastValue(repo.categories)
+            val systemCategory = categories?.firstOrNull { it.type == ShortcutCategoryType.System }
+
+            val expectedCategory =
+                ShortcutCategory(
+                    type = ShortcutCategoryType.System,
+                    simpleSubCategory(
+                        simpleDrawableModifierShortcut("1", modifierDrawable = metaDrawable)
+                    ),
+                )
+
+            assertThat(systemCategory).isEqualTo(expectedCategory)
+        }
+
+    @EnableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+    @Test
+    fun modifierMappedToDefaultDrawableWhenNoKeyGlyphMapExists() =
+        testScope.runTest {
+            fakeSystemSource.setGroups(simpleGroup(simpleShortcutInfo(KEYCODE_1, META_META_ON)))
+            helper.toggle(deviceId = 123)
+
+            val categories by collectLastValue(repo.categories)
+            val systemCategory = categories?.firstOrNull { it.type == ShortcutCategoryType.System }
+
+            val expectedCategory =
+                ShortcutCategory(
+                    type = ShortcutCategoryType.System,
+                    simpleSubCategory(
+                        simpleResIdModifierShortcut("1", modifierResId = R.drawable.ic_ksh_key_meta)
+                    ),
+                )
+            assertThat(systemCategory).isEqualTo(expectedCategory)
+        }
+
+    @DisableFlags(FLAG_SHORTCUT_HELPER_KEY_GLYPH)
+    @Test
+    fun modifierMappedToDefaultDrawableWhenKeyGlyphDisabled() =
+        testScope.runTest {
+            val metaDrawable = mock(Drawable::class.java)
+            val keyGlyph = mock(KeyGlyphMap::class.java)
+            whenever(keyGlyph.getDrawableForModifierState(context, META_META_ON))
+                .thenReturn(metaDrawable)
+            whenever(kosmos.fakeInputManager.inputManager.getKeyGlyphMap(anyInt()))
+                .thenReturn(keyGlyph)
+            fakeSystemSource.setGroups(simpleGroup(simpleShortcutInfo(KEYCODE_1, META_META_ON)))
+            helper.toggle(deviceId = 123)
+
+            val categories by collectLastValue(repo.categories)
+            val systemCategory = categories?.firstOrNull { it.type == ShortcutCategoryType.System }
+
+            val expectedCategory =
+                ShortcutCategory(
+                    type = ShortcutCategoryType.System,
+                    simpleSubCategory(
+                        simpleResIdModifierShortcut("1", modifierResId = R.drawable.ic_ksh_key_meta)
+                    ),
+                )
+            assertThat(systemCategory).isEqualTo(expectedCategory)
+        }
+
     private fun simpleSubCategory(vararg shortcuts: Shortcut) =
         ShortcutSubCategory(simpleGroupLabel, shortcuts.asList())
 
@@ -196,6 +279,37 @@
             commands = listOf(ShortcutCommand(keys.map { ShortcutKey.Text(it) })),
         )
 
+    private fun simpleDrawableModifierShortcut(
+        vararg keys: String,
+        modifierDrawable: Drawable,
+    ): Shortcut {
+        val keyShortcuts = keys.map { ShortcutKey.Text(it) }
+        return Shortcut(
+            label = simpleShortcutLabel,
+            commands =
+                listOf(
+                    ShortcutCommand(
+                        listOf(ShortcutKey.Icon.DrawableIcon(drawable = modifierDrawable)) +
+                            keyShortcuts
+                    )
+                ),
+        )
+    }
+
+    private fun simpleResIdModifierShortcut(vararg keys: String, modifierResId: Int): Shortcut {
+        val keyShortcuts = keys.map { ShortcutKey.Text(it) }
+        return Shortcut(
+            label = simpleShortcutLabel,
+            commands =
+                listOf(
+                    ShortcutCommand(
+                        listOf(ShortcutKey.Icon.ResIdIcon(drawableResId = modifierResId)) +
+                            keyShortcuts
+                    )
+                ),
+        )
+    }
+
     private fun simpleGroup(vararg shortcuts: KeyboardShortcutInfo) =
         KeyboardShortcutGroup(simpleGroupLabel, shortcuts.asList())
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index 9ca3ce6..e9e3e1b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -81,7 +81,7 @@
             this.fakeKeyguardTransitionRepository =
                 FakeKeyguardTransitionRepository(
                     // This test sends transition steps manually in the test cases.
-                    sendTransitionStepsOnStartTransition = false,
+                    initiallySendTransitionStepsOnStartTransition = false,
                     testScope = testScope,
                 )
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 9c2e631..b29a5f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -27,15 +27,13 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
-import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat as assertThatRepository
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.shade.data.repository.FlingInfo
 import com.android.systemui.shade.data.repository.fakeShadeRepository
@@ -48,6 +46,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
+import org.mockito.Mockito.spy
+import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat as assertThatRepository
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -55,7 +55,9 @@
 class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
-            this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
+            this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository(
+                testScope = testScope,
+            ))
         }
 
     private val testScope = kosmos.testScope
@@ -66,7 +68,7 @@
 
     @Before
     fun setup() {
-        transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
+        transitionRepository = kosmos.fakeKeyguardTransitionRepository
     }
 
     @Test
@@ -302,4 +304,74 @@
                     to = KeyguardState.LOCKSCREEN,
                 )
         }
+
+    /**
+     * External signals can cause us to transition from PRIMARY_BOUNCER -> * while a manual
+     * transition is in progress. This test was added after a bug that caused the manual transition
+     * ID to get stuck in this scenario, preventing subsequent transitions to PRIMARY_BOUNCER.
+     */
+    @Test
+    fun testExternalTransitionAwayFromBouncer_transitionIdNotStuck() =
+        testScope.runTest {
+            underTest.start()
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            keyguardRepository.setKeyguardDismissible(false)
+            shadeRepository.setLegacyShadeTracking(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            reset(transitionRepository)
+
+            // Disable automatic sending of transition steps so we can send steps through RUNNING
+            // to simulate a cancellation.
+            transitionRepository.sendTransitionStepsOnStartTransition = false
+            shadeRepository.setLegacyShadeExpansion(0.5f)
+            runCurrent()
+
+            assertThatRepository(transitionRepository)
+                .startedTransition(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                )
+
+            // Partially transition to PRIMARY_BOUNCER.
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.PRIMARY_BOUNCER,
+                throughTransitionState = TransitionState.RUNNING,
+                testScope = testScope,
+            )
+
+            // Start a transition to GONE, which will cancel LS -> BOUNCER.
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.PRIMARY_BOUNCER,
+                to = KeyguardState.GONE,
+                testScope = testScope,
+            )
+
+            // Go to AOD, then LOCKSCREEN.
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.AOD,
+                testScope = testScope,
+            )
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                testScope = testScope,
+            )
+
+            reset(transitionRepository)
+
+            // Start a swipe up to the bouncer, and verify that we started a transition to
+            // PRIMARY_BOUNCER, verifying the transition ID did not get stuck.
+            shadeRepository.setLegacyShadeExpansion(0.25f)
+            runCurrent()
+
+            assertThatRepository(transitionRepository)
+                .startedTransition(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.PRIMARY_BOUNCER,
+                )
+        }
 }
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/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index a8bb2b0..46d1ebe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.dock.DockManager
 import com.android.systemui.dock.DockManagerFake
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
@@ -54,6 +55,7 @@
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.cameraLauncher
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -117,8 +119,8 @@
                     BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
                     ":" +
-                    BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
-            )
+                    BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET,
+            ),
         )
 
         repository = FakeKeyguardRepository()
@@ -141,13 +143,7 @@
                 context = context,
                 userFileManager =
                     mock<UserFileManager>().apply {
-                        whenever(
-                                getSharedPreferences(
-                                    anyString(),
-                                    anyInt(),
-                                    anyInt(),
-                                )
-                            )
+                        whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
@@ -181,10 +177,7 @@
         featureFlags = FakeFeatureFlags()
 
         val withDeps =
-            KeyguardInteractorFactory.create(
-                featureFlags = featureFlags,
-                repository = repository,
-            )
+            KeyguardInteractorFactory.create(featureFlags = featureFlags, repository = repository)
         underTest =
             KeyguardQuickAffordanceInteractor(
                 keyguardInteractor = withDeps.keyguardInteractor,
@@ -205,6 +198,7 @@
                 appContext = context,
                 sceneInteractor = { kosmos.sceneInteractor },
             )
+        kosmos.keyguardQuickAffordanceInteractor = underTest
 
         whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f))
     }
@@ -241,9 +235,7 @@
         testScope.runTest {
             val configKey = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
             quickAccessWallet.setState(
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon = ICON,
-                )
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
             )
 
             val collectedValue =
@@ -268,9 +260,7 @@
             whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
             quickAccessWallet.setState(
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon = ICON,
-                )
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
             )
 
             val collectedValue by
@@ -287,9 +277,7 @@
             whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
             quickAccessWallet.setState(
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon = ICON,
-                )
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
             )
 
             val collectedValue by
@@ -305,9 +293,7 @@
         testScope.runTest {
             biometricSettingsRepository.setIsUserInLockdown(true)
             quickAccessWallet.setState(
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon = ICON,
-                )
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
             )
 
             val collectedValue by
@@ -323,9 +309,7 @@
         testScope.runTest {
             repository.setIsDozing(true)
             homeControls.setState(
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon = ICON,
-                )
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
             )
 
             val collectedValue =
@@ -340,9 +324,7 @@
         testScope.runTest {
             repository.setKeyguardShowing(false)
             homeControls.setState(
-                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
-                    icon = ICON,
-                )
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
             )
 
             val collectedValue =
@@ -446,7 +428,7 @@
             val collectedValue =
                 collectLastValue(
                     underTest.quickAffordanceAlwaysVisible(
-                        KeyguardQuickAffordancePosition.BOTTOM_START,
+                        KeyguardQuickAffordancePosition.BOTTOM_START
                     )
                 )
             assertThat(collectedValue())
@@ -487,10 +469,7 @@
     @Test
     fun select() =
         testScope.runTest {
-            overrideResource(
-                R.array.config_keyguardQuickAffordanceDefaults,
-                arrayOf<String>(),
-            )
+            overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
             homeControls.setState(
                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
             )
@@ -530,10 +509,7 @@
                         activationState = ActivationState.NotSupported,
                     )
                 )
-            assertThat(endConfig())
-                .isEqualTo(
-                    KeyguardQuickAffordanceModel.Hidden,
-                )
+            assertThat(endConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
             assertThat(underTest.getSelections())
                 .isEqualTo(
                     mapOf(
@@ -543,7 +519,7 @@
                                     id = homeControls.key,
                                     name = homeControls.pickerName(),
                                     iconResourceId = homeControls.pickerIconResourceId,
-                                ),
+                                )
                             ),
                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
                     )
@@ -551,7 +527,7 @@
 
             underTest.select(
                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
-                quickAccessWallet.key
+                quickAccessWallet.key,
             )
 
             assertThat(startConfig())
@@ -564,10 +540,7 @@
                         activationState = ActivationState.NotSupported,
                     )
                 )
-            assertThat(endConfig())
-                .isEqualTo(
-                    KeyguardQuickAffordanceModel.Hidden,
-                )
+            assertThat(endConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
             assertThat(underTest.getSelections())
                 .isEqualTo(
                     mapOf(
@@ -577,7 +550,7 @@
                                     id = quickAccessWallet.key,
                                     name = quickAccessWallet.pickerName(),
                                     iconResourceId = quickAccessWallet.pickerIconResourceId,
-                                ),
+                                )
                             ),
                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
                     )
@@ -614,7 +587,7 @@
                                     id = quickAccessWallet.key,
                                     name = quickAccessWallet.pickerName(),
                                     iconResourceId = quickAccessWallet.pickerIconResourceId,
-                                ),
+                                )
                             ),
                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
                             listOf(
@@ -622,7 +595,7 @@
                                     id = qrCodeScanner.key,
                                     name = qrCodeScanner.pickerName(),
                                     iconResourceId = qrCodeScanner.pickerIconResourceId,
-                                ),
+                                )
                             ),
                     )
                 )
@@ -653,10 +626,7 @@
             underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, quickAccessWallet.key)
             underTest.unselect(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, homeControls.key)
 
-            assertThat(startConfig())
-                .isEqualTo(
-                    KeyguardQuickAffordanceModel.Hidden,
-                )
+            assertThat(startConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
             assertThat(endConfig())
                 .isEqualTo(
                     KeyguardQuickAffordanceModel.Visible(
@@ -677,24 +647,18 @@
                                     id = quickAccessWallet.key,
                                     name = quickAccessWallet.pickerName(),
                                     iconResourceId = quickAccessWallet.pickerIconResourceId,
-                                ),
+                                )
                             ),
                     )
                 )
 
             underTest.unselect(
                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
-                quickAccessWallet.key
+                quickAccessWallet.key,
             )
 
-            assertThat(startConfig())
-                .isEqualTo(
-                    KeyguardQuickAffordanceModel.Hidden,
-                )
-            assertThat(endConfig())
-                .isEqualTo(
-                    KeyguardQuickAffordanceModel.Hidden,
-                )
+            assertThat(startConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
+            assertThat(endConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
             assertThat(underTest.getSelections())
                 .isEqualTo(
                     mapOf<String, List<String>>(
@@ -768,15 +732,12 @@
                                     id = quickAccessWallet.key,
                                     name = quickAccessWallet.pickerName(),
                                     iconResourceId = quickAccessWallet.pickerIconResourceId,
-                                ),
+                                )
                             ),
                     )
                 )
 
-            underTest.unselect(
-                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
-                null,
-            )
+            underTest.unselect(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, null)
 
             assertThat(underTest.getSelections())
                 .isEqualTo(
@@ -787,15 +748,29 @@
                 )
         }
 
+    @EnableSceneContainer
+    @Test
+    fun updatesLaunchingAffordanceFromCameraLauncher() =
+        testScope.runTest {
+            val launchingAffordance by collectLastValue(underTest.launchingAffordance)
+            runCurrent()
+
+            kosmos.cameraLauncher.setLaunchingAffordance(true)
+            runCurrent()
+            assertThat(launchingAffordance).isTrue()
+
+            kosmos.cameraLauncher.setLaunchingAffordance(false)
+            runCurrent()
+            assertThat(launchingAffordance).isFalse()
+        }
+
     companion object {
         private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
         private val ICON: Icon =
             Icon.Resource(
                 res = CONTENT_DESCRIPTION_RESOURCE_ID,
                 contentDescription =
-                    ContentDescription.Resource(
-                        res = CONTENT_DESCRIPTION_RESOURCE_ID,
-                    ),
+                    ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID),
             )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 12eadfc..b5e670c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.flags.parameterizeSceneContainerFlag
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -82,6 +83,7 @@
     private val communalRepository by lazy { kosmos.communalSceneRepository }
     private val screenOffAnimationController by lazy { kosmos.screenOffAnimationController }
     private val deviceEntryRepository by lazy { kosmos.fakeDeviceEntryRepository }
+    private val pulseExpansionInteractor by lazy { kosmos.pulseExpansionInteractor }
     private val notificationsKeyguardInteractor by lazy { kosmos.notificationsKeyguardInteractor }
     private val dozeParameters by lazy { kosmos.dozeParameters }
     private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
@@ -188,7 +190,7 @@
         testScope.runTest {
             val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
             runCurrent()
-            notificationsKeyguardInteractor.setPulseExpanding(true)
+            pulseExpansionInteractor.setPulseExpanding(true)
             deviceEntryRepository.setBypassEnabled(false)
             runCurrent()
 
@@ -200,7 +202,7 @@
         testScope.runTest {
             val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
             runCurrent()
-            notificationsKeyguardInteractor.setPulseExpanding(false)
+            pulseExpansionInteractor.setPulseExpanding(false)
             deviceEntryRepository.setBypassEnabled(true)
             notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
             runCurrent()
@@ -219,7 +221,7 @@
                 to = KeyguardState.DOZING,
                 testScope,
             )
-            notificationsKeyguardInteractor.setPulseExpanding(false)
+            pulseExpansionInteractor.setPulseExpanding(false)
             deviceEntryRepository.setBypassEnabled(false)
             whenever(dozeParameters.alwaysOn).thenReturn(false)
             notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
@@ -239,7 +241,7 @@
                 to = KeyguardState.DOZING,
                 testScope,
             )
-            notificationsKeyguardInteractor.setPulseExpanding(false)
+            pulseExpansionInteractor.setPulseExpanding(false)
             deviceEntryRepository.setBypassEnabled(false)
             whenever(dozeParameters.alwaysOn).thenReturn(true)
             whenever(dozeParameters.displayNeedsBlanking).thenReturn(true)
@@ -260,7 +262,7 @@
                 to = KeyguardState.DOZING,
                 testScope,
             )
-            notificationsKeyguardInteractor.setPulseExpanding(false)
+            pulseExpansionInteractor.setPulseExpanding(false)
             deviceEntryRepository.setBypassEnabled(false)
             whenever(dozeParameters.alwaysOn).thenReturn(true)
             whenever(dozeParameters.displayNeedsBlanking).thenReturn(false)
@@ -276,7 +278,7 @@
         testScope.runTest {
             val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
             runCurrent()
-            notificationsKeyguardInteractor.setPulseExpanding(false)
+            pulseExpansionInteractor.setPulseExpanding(false)
             deviceEntryRepository.setBypassEnabled(true)
             whenever(dozeParameters.alwaysOn).thenReturn(true)
             whenever(dozeParameters.displayNeedsBlanking).thenReturn(false)
@@ -298,7 +300,7 @@
         testScope.runTest {
             val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
             runCurrent()
-            notificationsKeyguardInteractor.setPulseExpanding(false)
+            pulseExpansionInteractor.setPulseExpanding(false)
             deviceEntryRepository.setBypassEnabled(false)
             whenever(dozeParameters.alwaysOn).thenReturn(true)
             whenever(dozeParameters.displayNeedsBlanking).thenReturn(false)
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..b5fc52f 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,24 +19,31 @@
 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
 import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.domain.pipeline.legacyMediaDataManagerImpl
+import com.android.systemui.media.controls.domain.pipeline.mediaDataManager
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestResult
 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.After
 import org.junit.Before
+import org.junit.runner.RunWith
 
+@RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 abstract class AbstractQSFragmentComposeViewModelTest : SysuiTestCase() {
-    protected val kosmos = testKosmos()
+    protected val kosmos = testKosmos().apply { mediaDataManager = legacyMediaDataManagerImpl }
 
     protected val lifecycleOwner =
         TestLifecycleOwner(
@@ -59,11 +66,15 @@
     }
 
     protected inline fun TestScope.testWithinLifecycle(
-        crossinline block: suspend TestScope.() -> TestResult
+        usingMedia: Boolean = true,
+        crossinline block: suspend TestScope.() -> TestResult,
     ): TestResult {
         return runTest {
+            kosmos.usingMediaInComposeFragment = usingMedia
+
             lifecycleOwner.setCurrentState(Lifecycle.State.RESUMED)
             underTest.activateIn(kosmos.testScope)
+            runCurrent()
             block().also { lifecycleOwner.setCurrentState(Lifecycle.State.DESTROYED) }
         }
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
index 3b00f86..9fe9ed2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -25,6 +25,15 @@
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.domain.pipeline.legacyMediaDataManagerImpl
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.mediaCarouselController
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.qqsMediaHost
+import com.android.systemui.media.controls.ui.view.qsMediaHost
+import com.android.systemui.qs.composefragment.viewmodel.MediaState.ACTIVE_MEDIA
+import com.android.systemui.qs.composefragment.viewmodel.MediaState.ANY_MEDIA
+import com.android.systemui.qs.composefragment.viewmodel.MediaState.NO_MEDIA
 import com.android.systemui.qs.fgsManagerController
 import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
 import com.android.systemui.res.R
@@ -35,9 +44,11 @@
 import com.android.systemui.statusbar.sysuiStatusBarStateController
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -185,6 +196,92 @@
             }
         }
 
+    @Test
+    fun qqsMediaHost_initializedCorrectly() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                assertThat(underTest.qqsMediaHost.location)
+                    .isEqualTo(MediaHierarchyManager.LOCATION_QQS)
+                assertThat(underTest.qqsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED)
+                assertThat(underTest.qqsMediaHost.showsOnlyActiveMedia).isTrue()
+                assertThat(underTest.qqsMediaHost.hostView).isNotNull()
+            }
+        }
+
+    @Test
+    fun qsMediaHost_initializedCorrectly() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                assertThat(underTest.qsMediaHost.location)
+                    .isEqualTo(MediaHierarchyManager.LOCATION_QS)
+                assertThat(underTest.qsMediaHost.expansion).isEqualTo(MediaHostState.EXPANDED)
+                assertThat(underTest.qsMediaHost.showsOnlyActiveMedia).isFalse()
+                assertThat(underTest.qsMediaHost.hostView).isNotNull()
+            }
+        }
+
+    @Test
+    fun qqsMediaVisible_onlyWhenActiveMedia() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                whenever(mediaCarouselController.isLockedAndHidden()).thenReturn(false)
+
+                assertThat(underTest.qqsMediaVisible).isEqualTo(underTest.qqsMediaHost.visible)
+
+                setMediaState(NO_MEDIA)
+                assertThat(underTest.qqsMediaVisible).isFalse()
+
+                setMediaState(ANY_MEDIA)
+                assertThat(underTest.qqsMediaVisible).isFalse()
+
+                setMediaState(ACTIVE_MEDIA)
+                assertThat(underTest.qqsMediaVisible).isTrue()
+            }
+        }
+
+    @Test
+    fun qsMediaVisible_onAnyMedia() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                whenever(mediaCarouselController.isLockedAndHidden()).thenReturn(false)
+
+                assertThat(underTest.qsMediaVisible).isEqualTo(underTest.qsMediaHost.visible)
+
+                setMediaState(NO_MEDIA)
+                assertThat(underTest.qsMediaVisible).isFalse()
+
+                setMediaState(ANY_MEDIA)
+                assertThat(underTest.qsMediaVisible).isTrue()
+
+                setMediaState(ACTIVE_MEDIA)
+                assertThat(underTest.qsMediaVisible).isTrue()
+            }
+        }
+
+    @Test
+    fun notUsingMedia_mediaNotVisible() =
+        with(kosmos) {
+            testScope.testWithinLifecycle(usingMedia = false) {
+                setMediaState(ACTIVE_MEDIA)
+
+                assertThat(underTest.qqsMediaVisible).isFalse()
+                assertThat(underTest.qsMediaVisible).isFalse()
+            }
+        }
+
+    private fun TestScope.setMediaState(state: MediaState) {
+        with(kosmos) {
+            val activeMedia = state == ACTIVE_MEDIA
+            val anyMedia = state != NO_MEDIA
+            whenever(legacyMediaDataManagerImpl.hasActiveMediaOrRecommendation())
+                .thenReturn(activeMedia)
+            whenever(legacyMediaDataManagerImpl.hasAnyMediaOrRecommendation()).thenReturn(anyMedia)
+            qqsMediaHost.updateViewVisibility()
+            qsMediaHost.updateViewVisibility()
+        }
+        runCurrent()
+    }
+
     companion object {
         private const val QS_DISABLE_FLAG = StatusBarManager.DISABLE2_QUICK_SETTINGS
 
@@ -195,3 +292,9 @@
         private const val epsilon = 0.001f
     }
 }
+
+private enum class MediaState {
+    ACTIVE_MEDIA,
+    ANY_MEDIA,
+    NO_MEDIA,
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
index 2c894f9..ab5a049 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
@@ -20,21 +20,25 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.qs.panels.domain.interactor.qsPreferencesInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.res.R
 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.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class QuickQuickSettingsViewModelTest : SysuiTestCase() {
@@ -65,7 +69,8 @@
             fakeConfigurationRepository.onConfigurationChange()
         }
 
-    private val underTest = kosmos.quickQuickSettingsViewModel
+    private val underTest =
+        kosmos.quickQuickSettingsViewModelFactory.create().apply { activateIn(kosmos.testScope) }
 
     @Before
     fun setUp() {
@@ -77,17 +82,15 @@
         with(kosmos) {
             testScope.runTest {
                 setRows(2)
-                val columns by collectLastValue(underTest.columns)
-                val tileViewModels by collectLastValue(underTest.tileViewModels)
 
-                assertThat(columns).isEqualTo(4)
+                assertThat(underTest.columns).isEqualTo(4)
                 // All tiles in 4 columns
                 // [1] [2] [3 3]
                 // [4] [5 5]
                 // [6 6] [7] [8]
                 // [9 9]
 
-                assertThat(tileViewModels!!.map { it.tile.spec }).isEqualTo(tiles.take(5))
+                assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(5))
             }
         }
 
@@ -96,10 +99,8 @@
         with(kosmos) {
             testScope.runTest {
                 setRows(2)
-                val columns by collectLastValue(underTest.columns)
-                val tileViewModels by collectLastValue(underTest.tileViewModels)
 
-                assertThat(columns).isEqualTo(4)
+                assertThat(underTest.columns).isEqualTo(4)
                 // All tiles in 4 columns
                 // [1] [2] [3 3]
                 // [4] [5 5]
@@ -107,9 +108,9 @@
                 // [9 9]
 
                 setRows(3)
-                assertThat(tileViewModels!!.map { it.tile.spec }).isEqualTo(tiles.take(8))
+                assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(8))
                 setRows(1)
-                assertThat(tileViewModels!!.map { it.tile.spec }).isEqualTo(tiles.take(3))
+                assertThat(underTest.tileViewModels.map { it.tile.spec }).isEqualTo(tiles.take(3))
             }
         }
 
@@ -118,10 +119,8 @@
         with(kosmos) {
             testScope.runTest {
                 setRows(2)
-                val columns by collectLastValue(underTest.columns)
-                val tileViewModels by collectLastValue(underTest.tileViewModels)
 
-                assertThat(columns).isEqualTo(4)
+                assertThat(underTest.columns).isEqualTo(4)
                 // All tiles in 4 columns
                 // [1] [2] [3 3]
                 // [4] [5 5]
@@ -130,8 +129,9 @@
 
                 // Remove tile small:4
                 currentTilesInteractor.removeTiles(setOf(tiles[3]))
+                runCurrent()
 
-                assertThat(tileViewModels!!.map { it.tile.spec })
+                assertThat(underTest.tileViewModels.map { it.tile.spec })
                     .isEqualTo(
                         listOf(
                                 "$PREFIX_SMALL:1",
@@ -149,12 +149,15 @@
         currentTilesInteractor.setTiles(tiles)
     }
 
-    private fun Kosmos.setRows(rows: Int) {
-        testCase.context.orCreateTestableResources.addOverride(
-            R.integer.quick_qs_paginated_grid_num_rows,
-            rows,
-        )
-        fakeConfigurationRepository.onConfigurationChange()
+    private fun TestScope.setRows(rows: Int) {
+        with(kosmos) {
+            testCase.context.orCreateTestableResources.addOverride(
+                R.integer.quick_qs_paginated_grid_num_rows,
+                rows,
+            )
+            fakeConfigurationRepository.onConfigurationChange()
+        }
+        runCurrent()
     }
 
     private companion object {
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/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index d2bf9b88..319f1e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -44,6 +44,8 @@
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.ui.viewmodel.lockscreenUserActionsViewModel
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -70,7 +72,6 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -101,7 +102,6 @@
 @EnableSceneContainer
 class SceneFrameworkIntegrationTest : SysuiTestCase() {
     private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
     private var bouncerSceneJob: Job? = null
 
     @Before
@@ -137,151 +137,149 @@
                 .isTrue()
         }
 
-    @Test
-    fun startsInLockscreenScene() =
-        testScope.runTest { kosmos.assertCurrentScene(Scenes.Lockscreen) }
+    @Test fun startsInLockscreenScene() = kosmos.runTest { assertCurrentScene(Scenes.Lockscreen) }
 
     @Test
     fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
-        testScope.runTest {
-            kosmos.emulateUserDrivenTransition(Scenes.Bouncer)
+        kosmos.runTest {
+            emulateUserDrivenTransition(Scenes.Bouncer)
 
-            kosmos.fakeSceneDataSource.pause()
-            kosmos.enterPin()
-            kosmos.emulatePendingTransitionProgress(expectedVisible = false)
-            kosmos.assertCurrentScene(Scenes.Gone)
+            fakeSceneDataSource.pause()
+            enterPin()
+            emulatePendingTransitionProgress(expectedVisible = false)
+            assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun swipeUpOnLockscreen_enterCorrectPin_unlocksDevice() =
-        testScope.runTest {
-            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+        kosmos.runTest {
+            val actions by testScope.collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
             val upDestinationSceneKey =
                 (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
-            kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+            emulateUserDrivenTransition(to = upDestinationSceneKey)
 
-            kosmos.fakeSceneDataSource.pause()
-            kosmos.enterPin()
-            kosmos.emulatePendingTransitionProgress(expectedVisible = false)
-            kosmos.assertCurrentScene(Scenes.Gone)
+            fakeSceneDataSource.pause()
+            enterPin()
+            emulatePendingTransitionProgress(expectedVisible = false)
+            assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun swipeUpOnLockscreen_withAuthMethodSwipe_dismissesLockscreen() =
-        testScope.runTest {
-            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+        kosmos.runTest {
+            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
 
-            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+            val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
             val upDestinationSceneKey =
                 (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
-            kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+            emulateUserDrivenTransition(to = upDestinationSceneKey)
         }
 
     @Test
     fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
-        testScope.runTest {
-            val actions by collectLastValue(kosmos.shadeUserActionsViewModel.actions)
-            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+        kosmos.runTest {
+            val actions by testScope.collectLastValue(shadeUserActionsViewModel.actions)
+            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+            assertCurrentScene(Scenes.Lockscreen)
 
             // Emulate a user swipe to the shade scene.
-            kosmos.emulateUserDrivenTransition(to = Scenes.Shade)
-            kosmos.assertCurrentScene(Scenes.Shade)
+            emulateUserDrivenTransition(to = Scenes.Shade)
+            assertCurrentScene(Scenes.Shade)
 
             val upDestinationSceneKey =
                 (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Lockscreen)
-            kosmos.emulateUserDrivenTransition(to = Scenes.Lockscreen)
+            emulateUserDrivenTransition(to = Scenes.Lockscreen)
         }
 
     @Test
     fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenDismissed_goesToGone() =
-        testScope.runTest {
-            val actions by collectLastValue(kosmos.shadeUserActionsViewModel.actions)
-            val canSwipeToEnter by collectLastValue(kosmos.deviceEntryInteractor.canSwipeToEnter)
+        kosmos.runTest {
+            val actions by testScope.collectLastValue(shadeUserActionsViewModel.actions)
+            val canSwipeToEnter by testScope.collectLastValue(deviceEntryInteractor.canSwipeToEnter)
 
-            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
 
             assertThat(canSwipeToEnter).isTrue()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+            assertCurrentScene(Scenes.Lockscreen)
 
             // Emulate a user swipe to dismiss the lockscreen.
-            kosmos.emulateUserDrivenTransition(to = Scenes.Gone)
-            kosmos.assertCurrentScene(Scenes.Gone)
+            emulateUserDrivenTransition(to = Scenes.Gone)
+            assertCurrentScene(Scenes.Gone)
 
             // Emulate a user swipe to the shade scene.
-            kosmos.emulateUserDrivenTransition(to = Scenes.Shade)
-            kosmos.assertCurrentScene(Scenes.Shade)
+            emulateUserDrivenTransition(to = Scenes.Shade)
+            assertCurrentScene(Scenes.Shade)
 
             val upDestinationSceneKey =
                 (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
-            kosmos.emulateUserDrivenTransition(to = Scenes.Gone)
+            emulateUserDrivenTransition(to = Scenes.Gone)
         }
 
     @Test
     fun withAuthMethodNone_deviceWakeUp_skipsLockscreen() =
-        testScope.runTest {
-            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
-            kosmos.putDeviceToSleep()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+        kosmos.runTest {
+            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
+            putDeviceToSleep()
+            assertCurrentScene(Scenes.Lockscreen)
 
-            kosmos.wakeUpDevice()
-            kosmos.assertCurrentScene(Scenes.Gone)
+            wakeUpDevice()
+            assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun withAuthMethodSwipe_deviceWakeUp_doesNotSkipLockscreen() =
-        testScope.runTest {
-            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
-            kosmos.putDeviceToSleep()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+        kosmos.runTest {
+            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+            putDeviceToSleep()
+            assertCurrentScene(Scenes.Lockscreen)
 
-            kosmos.wakeUpDevice()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+            wakeUpDevice()
+            assertCurrentScene(Scenes.Lockscreen)
         }
 
     @Test
     fun lockDeviceLocksDevice() =
-        testScope.runTest {
-            kosmos.unlockDevice()
-            kosmos.assertCurrentScene(Scenes.Gone)
+        kosmos.runTest {
+            unlockDevice()
+            assertCurrentScene(Scenes.Gone)
 
-            kosmos.lockDevice()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+            lockDevice()
+            assertCurrentScene(Scenes.Lockscreen)
         }
 
     @Test
     fun deviceGoesToSleep_switchesToLockscreen() =
-        testScope.runTest {
-            kosmos.unlockDevice()
-            kosmos.assertCurrentScene(Scenes.Gone)
+        kosmos.runTest {
+            unlockDevice()
+            assertCurrentScene(Scenes.Gone)
 
-            kosmos.putDeviceToSleep()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+            putDeviceToSleep()
+            assertCurrentScene(Scenes.Lockscreen)
         }
 
     @Test
     fun deviceGoesToSleep_wakeUp_unlock() =
-        testScope.runTest {
-            kosmos.unlockDevice()
-            kosmos.assertCurrentScene(Scenes.Gone)
-            kosmos.putDeviceToSleep()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
-            kosmos.wakeUpDevice()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+        kosmos.runTest {
+            unlockDevice()
+            assertCurrentScene(Scenes.Gone)
+            putDeviceToSleep()
+            assertCurrentScene(Scenes.Lockscreen)
+            wakeUpDevice()
+            assertCurrentScene(Scenes.Lockscreen)
 
-            kosmos.unlockDevice()
-            kosmos.assertCurrentScene(Scenes.Gone)
+            unlockDevice()
+            assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun swipeUpOnLockscreenWhileUnlocked_dismissesLockscreen() =
-        testScope.runTest {
-            kosmos.unlockDevice()
-            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+        kosmos.runTest {
+            unlockDevice()
+            val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
             val upDestinationSceneKey =
                 (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
@@ -289,50 +287,50 @@
 
     @Test
     fun deviceGoesToSleep_withLockTimeout_staysOnLockscreen() =
-        testScope.runTest {
-            kosmos.unlockDevice()
-            kosmos.assertCurrentScene(Scenes.Gone)
-            kosmos.putDeviceToSleep()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+        kosmos.runTest {
+            unlockDevice()
+            assertCurrentScene(Scenes.Gone)
+            putDeviceToSleep()
+            assertCurrentScene(Scenes.Lockscreen)
 
             // Pretend like the timeout elapsed and now lock the device.
-            kosmos.lockDevice()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+            lockDevice()
+            assertCurrentScene(Scenes.Lockscreen)
         }
 
     @Test
     fun dismissingIme_whileOnPasswordBouncer_navigatesToLockscreen() =
-        testScope.runTest {
-            kosmos.setAuthMethod(AuthenticationMethodModel.Password)
-            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+        kosmos.runTest {
+            setAuthMethod(AuthenticationMethodModel.Password)
+            val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
             val upDestinationSceneKey =
                 (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
-            kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+            emulateUserDrivenTransition(to = upDestinationSceneKey)
 
-            kosmos.fakeSceneDataSource.pause()
-            kosmos.dismissIme()
+            fakeSceneDataSource.pause()
+            dismissIme()
 
-            kosmos.emulatePendingTransitionProgress()
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+            emulatePendingTransitionProgress()
+            assertCurrentScene(Scenes.Lockscreen)
         }
 
     @Test
     fun bouncerActionButtonClick_opensEmergencyServicesDialer() =
-        testScope.runTest {
-            kosmos.setAuthMethod(AuthenticationMethodModel.Password)
-            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+        kosmos.runTest {
+            setAuthMethod(AuthenticationMethodModel.Password)
+            val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
             val upDestinationSceneKey =
                 (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
-            kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+            emulateUserDrivenTransition(to = upDestinationSceneKey)
 
             val bouncerActionButton by
-                collectLastValue(kosmos.bouncerSceneContentViewModel.actionButton)
+                testScope.collectLastValue(bouncerSceneContentViewModel.actionButton)
             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.
@@ -340,56 +338,56 @@
 
     @Test
     fun bouncerActionButtonClick_duringCall_returnsToCall() =
-        testScope.runTest {
-            kosmos.setAuthMethod(AuthenticationMethodModel.Password)
-            kosmos.startPhoneCall()
-            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+        kosmos.runTest {
+            setAuthMethod(AuthenticationMethodModel.Password)
+            startPhoneCall()
+            val actions by testScope.collectLastValue(lockscreenUserActionsViewModel.actions)
             val upDestinationSceneKey =
                 (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
-            kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
+            emulateUserDrivenTransition(to = upDestinationSceneKey)
 
             val bouncerActionButton by
-                collectLastValue(kosmos.bouncerSceneContentViewModel.actionButton)
+                testScope.collectLastValue(bouncerSceneContentViewModel.actionButton)
             assertWithMessage("Bouncer action button not visible during call")
                 .that(bouncerActionButton)
                 .isNotNull()
-            bouncerActionButton?.onClick?.invoke()
+            kosmos.bouncerSceneContentViewModel.onActionButtonClicked(bouncerActionButton!!)
             runCurrent()
 
-            verify(kosmos.mockTelecomManager).showInCallScreen(any())
+            verify(mockTelecomManager).showInCallScreen(any())
         }
 
     @Test
     fun showBouncer_whenLockedSimIntroduced() =
-        testScope.runTest {
-            kosmos.setAuthMethod(AuthenticationMethodModel.None)
-            kosmos.introduceLockedSim()
-            kosmos.assertCurrentScene(Scenes.Bouncer)
+        kosmos.runTest {
+            setAuthMethod(AuthenticationMethodModel.None)
+            introduceLockedSim()
+            assertCurrentScene(Scenes.Bouncer)
         }
 
     @Test
     fun goesToGone_whenSimUnlocked_whileDeviceUnlocked() =
-        testScope.runTest {
-            kosmos.fakeSceneDataSource.pause()
-            kosmos.introduceLockedSim()
-            kosmos.emulatePendingTransitionProgress(expectedVisible = true)
-            kosmos.enterSimPin(
+        kosmos.runTest {
+            fakeSceneDataSource.pause()
+            introduceLockedSim()
+            emulatePendingTransitionProgress(expectedVisible = true)
+            enterSimPin(
                 authMethodAfterSimUnlock = AuthenticationMethodModel.None,
                 enableLockscreen = false,
             )
 
-            kosmos.assertCurrentScene(Scenes.Gone)
+            assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun showLockscreen_whenSimUnlocked_whileDeviceLocked() =
-        testScope.runTest {
-            kosmos.fakeSceneDataSource.pause()
-            kosmos.introduceLockedSim()
-            kosmos.emulatePendingTransitionProgress(expectedVisible = true)
-            kosmos.enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
-            kosmos.assertCurrentScene(Scenes.Lockscreen)
+        kosmos.runTest {
+            fakeSceneDataSource.pause()
+            introduceLockedSim()
+            emulatePendingTransitionProgress(expectedVisible = true)
+            enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
+            assertCurrentScene(Scenes.Lockscreen)
         }
 
     /**
@@ -457,10 +455,10 @@
      */
     private fun Kosmos.emulatePendingTransitionProgress(expectedVisible: Boolean = true) {
         assertWithMessage("The FakeSceneDataSource has to be paused for this to do anything.")
-            .that(kosmos.fakeSceneDataSource.isPaused)
+            .that(fakeSceneDataSource.isPaused)
             .isTrue()
 
-        val to = kosmos.fakeSceneDataSource.pendingScene ?: return
+        val to = fakeSceneDataSource.pendingScene ?: return
         val from = getCurrentSceneInUi()
 
         if (to == from) {
@@ -489,7 +487,7 @@
         // End the transition and report the change.
         transitionState.value = ObservableTransitionState.Idle(to)
 
-        kosmos.fakeSceneDataSource.unpause(force = true)
+        fakeSceneDataSource.unpause(force = true)
         testScope.runCurrent()
 
         assertWithMessage("Visibility mismatch after scene transition from $from to $to!")
@@ -523,7 +521,7 @@
     private fun Kosmos.emulateUserDrivenTransition(to: SceneKey?) {
         checkNotNull(to)
 
-        kosmos.fakeSceneDataSource.pause()
+        fakeSceneDataSource.pause()
         sceneInteractor.changeScene(to, "reason")
 
         emulatePendingTransitionProgress(expectedVisible = to != Scenes.Gone)
@@ -634,7 +632,7 @@
     }
 
     /** Changes device wakefulness state from awake to asleep, going through intermediary states. */
-    private suspend fun Kosmos.putDeviceToSleep() {
+    private fun Kosmos.putDeviceToSleep() {
         val wakefulnessModel = powerInteractor.detailedWakefulness.value
         assertWithMessage("Cannot put device to sleep as it's already asleep!")
             .that(wakefulnessModel.isAwake())
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 4995920..2c8f7cf 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
@@ -32,6 +32,7 @@
 import com.android.internal.logging.uiEventLoggerFake
 import com.android.internal.policy.IKeyguardDismissCallback
 import com.android.keyguard.AuthInteractionProperties
+import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
@@ -71,8 +72,6 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.scenetransition.lockscreenSceneTransitionInteractor
-import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
-import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
@@ -128,6 +127,7 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -159,7 +159,8 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
+        whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+            .thenReturn(true)
         underTest = kosmos.sceneContainerStartable
     }
 
@@ -405,10 +406,7 @@
             assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)
             underTest.start()
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
-
+            updateFingerprintAuthStatus(isSuccess = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
@@ -430,10 +428,7 @@
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
             underTest.start()
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
-
+            updateFingerprintAuthStatus(isSuccess = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
             assertThat(alternateBouncerVisible).isFalse()
         }
@@ -464,9 +459,7 @@
             runCurrent()
             assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
+            updateFingerprintAuthStatus(isSuccess = true)
             runCurrent()
 
             assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
@@ -501,10 +494,7 @@
             assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)
             assertThat(backStack?.asIterable()?.last()).isEqualTo(Scenes.Lockscreen)
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
-
+            updateFingerprintAuthStatus(isSuccess = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
             assertThat(backStack?.asIterable()?.last()).isEqualTo(Scenes.Gone)
         }
@@ -535,10 +525,7 @@
             runCurrent()
             assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
-
+            updateFingerprintAuthStatus(isSuccess = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
@@ -554,10 +541,7 @@
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
             underTest.start()
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
-
+            updateFingerprintAuthStatus(isSuccess = true)
             assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
@@ -592,9 +576,7 @@
             transitionStateFlowValue.value = ObservableTransitionState.Idle(Scenes.Shade)
             assertThat(currentSceneKey).isEqualTo(Scenes.Shade)
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
+            updateFingerprintAuthStatus(isSuccess = true)
             runCurrent()
 
             assertThat(currentSceneKey).isEqualTo(Scenes.Shade)
@@ -786,6 +768,7 @@
     @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
     fun playSuccessHaptics_onSuccessfulLockscreenAuth_udfps() =
         testScope.runTest {
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
                 collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
@@ -795,24 +778,19 @@
             assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
 
             underTest.start()
-            unlockWithFingerprintAuth()
+            // unlock with fingerprint
+            updateFingerprintAuthStatus(isSuccess = true)
 
             assertThat(playSuccessHaptic).isNotNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            verify(vibratorHelper)
-                .vibrateAuthSuccess(
-                    "SceneContainerStartable, $currentSceneKey device-entry::success"
-                )
+            verify(vibratorHelper).vibrateAuthSuccess(anyString())
             verify(vibratorHelper, never()).vibrateAuthError(anyString())
-
-            updateFingerprintAuthStatus(isSuccess = true)
-            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
     @Test
     @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
     fun playSuccessMSDLHaptics_onSuccessfulLockscreenAuth_udfps() =
         testScope.runTest {
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
                 collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
@@ -822,21 +800,19 @@
             assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
 
             underTest.start()
-            unlockWithFingerprintAuth()
+            // unlock with fingerprint
+            updateFingerprintAuthStatus(isSuccess = true)
 
             assertThat(playSuccessHaptic).isNotNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
             assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.UNLOCK)
             assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
-
-            updateFingerprintAuthStatus(isSuccess = true)
-            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
     @Test
     @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
     fun playSuccessHaptics_onSuccessfulLockscreenAuth_sfps() =
         testScope.runTest {
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
                 collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
@@ -847,24 +823,19 @@
 
             underTest.start()
             allowHapticsOnSfps()
-            unlockWithFingerprintAuth()
+            // unlock with fingerprint
+            updateFingerprintAuthStatus(isSuccess = true)
 
             assertThat(playSuccessHaptic).isNotNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            verify(vibratorHelper)
-                .vibrateAuthSuccess(
-                    "SceneContainerStartable, $currentSceneKey device-entry::success"
-                )
+            verify(vibratorHelper).vibrateAuthSuccess(anyString())
             verify(vibratorHelper, never()).vibrateAuthError(anyString())
-
-            updateFingerprintAuthStatus(isSuccess = true)
-            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
     @Test
     @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
     fun playSuccessMSDLHaptics_onSuccessfulLockscreenAuth_sfps() =
         testScope.runTest {
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
                 collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
@@ -875,15 +846,12 @@
 
             underTest.start()
             allowHapticsOnSfps()
-            unlockWithFingerprintAuth()
+            // unlock with fingerprint
+            updateFingerprintAuthStatus(isSuccess = true)
 
             assertThat(playSuccessHaptic).isNotNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
             assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.UNLOCK)
             assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
-
-            updateFingerprintAuthStatus(isSuccess = true)
-            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
     @Test
@@ -902,8 +870,7 @@
 
             assertThat(playErrorHaptic).isNotNull()
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            verify(vibratorHelper)
-                .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+            verify(vibratorHelper).vibrateAuthError(anyString())
             verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
         }
 
@@ -943,8 +910,7 @@
 
             assertThat(playErrorHaptic).isNotNull()
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            verify(vibratorHelper)
-                .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+            verify(vibratorHelper).vibrateAuthError(anyString())
             verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
         }
 
@@ -972,6 +938,7 @@
     @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
     fun skipsSuccessHaptics_whenPowerButtonDown_sfps() =
         testScope.runTest {
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
                 collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
@@ -982,24 +949,19 @@
 
             underTest.start()
             allowHapticsOnSfps(isPowerButtonDown = true)
-            unlockWithFingerprintAuth()
+            // unlock with fingerprint
+            updateFingerprintAuthStatus(isSuccess = true)
 
             assertThat(playSuccessHaptic).isNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            verify(vibratorHelper, never())
-                .vibrateAuthSuccess(
-                    "SceneContainerStartable, $currentSceneKey device-entry::success"
-                )
+            verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
             verify(vibratorHelper, never()).vibrateAuthError(anyString())
-
-            updateFingerprintAuthStatus(isSuccess = true)
-            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
     @Test
     @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
     fun skipsMSDLSuccessHaptics_whenPowerButtonDown_sfps() =
         testScope.runTest {
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
                 collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
@@ -1010,21 +972,19 @@
 
             underTest.start()
             allowHapticsOnSfps(isPowerButtonDown = true)
-            unlockWithFingerprintAuth()
+            // unlock with fingerprint
+            updateFingerprintAuthStatus(isSuccess = true)
 
             assertThat(playSuccessHaptic).isNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
             assertThat(msdlPlayer.latestTokenPlayed).isNull()
             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
-
-            updateFingerprintAuthStatus(isSuccess = true)
-            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
     @Test
     @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
     fun skipsSuccessHaptics_whenPowerButtonRecentlyPressed_sfps() =
         testScope.runTest {
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
                 collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
@@ -1035,24 +995,19 @@
 
             underTest.start()
             allowHapticsOnSfps(lastPowerPress = 50)
-            unlockWithFingerprintAuth()
+            // unlock with fingerprint
+            updateFingerprintAuthStatus(isSuccess = true)
 
             assertThat(playSuccessHaptic).isNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            verify(vibratorHelper, never())
-                .vibrateAuthSuccess(
-                    "SceneContainerStartable, $currentSceneKey device-entry::success"
-                )
+            verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
             verify(vibratorHelper, never()).vibrateAuthError(anyString())
-
-            updateFingerprintAuthStatus(isSuccess = true)
-            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
     @Test
     @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
     fun skipsMSDLSuccessHaptics_whenPowerButtonRecentlyPressed_sfps() =
         testScope.runTest {
+            whenever(kosmos.keyguardUpdateMonitor.isDeviceInteractive).thenReturn(true)
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
             val playSuccessHaptic by
                 collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
@@ -1063,15 +1018,12 @@
 
             underTest.start()
             allowHapticsOnSfps(lastPowerPress = 50)
-            unlockWithFingerprintAuth()
+            // unlock with fingerprint
+            updateFingerprintAuthStatus(isSuccess = true)
 
             assertThat(playSuccessHaptic).isNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
             assertThat(msdlPlayer.latestTokenPlayed).isNull()
             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
-
-            updateFingerprintAuthStatus(isSuccess = true)
-            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
         }
 
     @Test
@@ -1090,9 +1042,7 @@
             updateFingerprintAuthStatus(isSuccess = false)
 
             assertThat(playErrorHaptic).isNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            verify(vibratorHelper, never())
-                .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+            verify(vibratorHelper, never()).vibrateAuthError(anyString())
             verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
         }
 
@@ -1112,7 +1062,6 @@
             updateFingerprintAuthStatus(isSuccess = false)
 
             assertThat(playErrorHaptic).isNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
             assertThat(msdlPlayer.latestTokenPlayed).isNull()
             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
         }
@@ -1132,9 +1081,7 @@
             updateFaceAuthStatus(isSuccess = false)
 
             assertThat(playErrorHaptic).isNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
-            verify(vibratorHelper, never())
-                .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+            verify(vibratorHelper, never()).vibrateAuthError(anyString())
             verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
         }
 
@@ -1153,7 +1100,6 @@
             updateFaceAuthStatus(isSuccess = false)
 
             assertThat(playErrorHaptic).isNull()
-            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
             assertThat(msdlPlayer.latestTokenPlayed).isNull()
             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
         }
@@ -1176,9 +1122,7 @@
                 )
                 .forEachIndexed { index, sceneKey ->
                     if (sceneKey == Scenes.Gone) {
-                        kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                            SuccessFingerprintAuthenticationStatus(0, true)
-                        )
+                        updateFingerprintAuthStatus(isSuccess = true)
                         runCurrent()
                     }
                     fakeSceneDataSource.pause()
@@ -1334,9 +1278,7 @@
             assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
             underTest.start()
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
+            updateFingerprintAuthStatus(isSuccess = true)
             runCurrent()
             powerInteractor.setAwakeForTest()
             runCurrent()
@@ -1586,9 +1528,7 @@
             runCurrent()
             verify(falsingCollector).onBouncerShown()
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
+            updateFingerprintAuthStatus(isSuccess = true)
             runCurrent()
             sceneInteractor.changeScene(Scenes.Gone, "reason")
             runCurrent()
@@ -2350,9 +2290,7 @@
             val dismissCallback: IKeyguardDismissCallback = mock()
             kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
 
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
+            updateFingerprintAuthStatus(isSuccess = true)
             runCurrent()
             kosmos.fakeExecutor.runAllReady()
 
@@ -2641,13 +2579,6 @@
         runCurrent()
     }
 
-    private fun unlockWithFingerprintAuth() {
-        kosmos.fakeKeyguardRepository.setBiometricUnlockSource(
-            BiometricUnlockSource.FINGERPRINT_SENSOR
-        )
-        kosmos.fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockMode.UNLOCK_COLLAPSING)
-    }
-
     private fun TestScope.setupBiometricAuth(
         hasSfps: Boolean = false,
         hasUdfps: Boolean = false,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index b632a8a..af895c8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -21,6 +21,8 @@
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_OUTSIDE
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -43,6 +45,7 @@
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -68,6 +71,7 @@
     private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
     private val fakeShadeRepository by lazy { kosmos.fakeShadeRepository }
     private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
+    private val fakeRemoteInputRepository by lazy { kosmos.fakeRemoteInputRepository }
     private val falsingManager by lazy { kosmos.fakeFalsingManager }
     private val view = mock<View>()
 
@@ -234,6 +238,35 @@
         }
 
     @Test
+    fun userInputOnEmptySpace_insideEvent() =
+        testScope.runTest {
+            assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+            val insideMotionEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0f, 0f, 0)
+            underTest.onEmptySpaceMotionEvent(insideMotionEvent)
+            assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+        }
+
+    @Test
+    fun userInputOnEmptySpace_outsideEvent_remoteInputActive() =
+        testScope.runTest {
+            fakeRemoteInputRepository.isRemoteInputActive.value = true
+            assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+            val outsideMotionEvent = MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)
+            underTest.onEmptySpaceMotionEvent(outsideMotionEvent)
+            assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isTrue()
+        }
+
+    @Test
+    fun userInputOnEmptySpace_outsideEvent_remoteInputInactive() =
+        testScope.runTest {
+            fakeRemoteInputRepository.isRemoteInputActive.value = false
+            assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+            val outsideMotionEvent = MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)
+            underTest.onEmptySpaceMotionEvent(outsideMotionEvent)
+            assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+        }
+
+    @Test
     fun remoteUserInteraction_keepsContainerVisible() =
         testScope.runTest {
             sceneInteractor.setVisible(false, "reason")
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/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..01c17bd 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;
@@ -211,6 +212,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.mockito.stubbing.Answer;
@@ -373,6 +375,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();
@@ -511,7 +516,8 @@
         when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
         when(mView.findViewById(R.id.keyguard_status_view))
                 .thenReturn(mock(KeyguardStatusView.class));
-        View rootView = mock(View.class);
+        ViewGroup rootView = mock(ViewGroup.class);
+        when(rootView.isVisibleToUser()).thenReturn(true);
         when(mView.getRootView()).thenReturn(rootView);
         when(rootView.findViewById(R.id.keyguard_status_view))
                 .thenReturn(mock(KeyguardStatusView.class));
@@ -613,7 +619,8 @@
                         mScreenOffAnimationController,
                         new NotificationWakeUpCoordinatorLogger(logcatLogBuffer()),
                         notifsKeyguardInteractor,
-                        mKosmos.getCommunalInteractor());
+                        mKosmos.getCommunalInteractor(),
+                        mKosmos.getPulseExpansionInteractor());
         mConfigurationController = new ConfigurationControllerImpl(mContext);
         PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
                 mContext,
@@ -648,12 +655,21 @@
             ((Runnable) invocation.getArgument(0)).run();
             return null;
         }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
+        when(mNotificationShadeWindowController.getWindowRootView()).thenReturn(rootView);
         doAnswer(invocation -> {
             mLayoutChangeListener = invocation.getArgument(0);
             return null;
         }).when(mView).addOnLayoutChangeListener(any());
 
         when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                ViewTreeObserver.OnGlobalLayoutListener gll = invocation.getArgument(0);
+                gll.onGlobalLayout();
+                return null;
+            }
+        }).when(mViewTreeObserver).addOnGlobalLayoutListener(any());
         when(mView.getParent()).thenReturn(mViewParent);
         when(mQs.getHeader()).thenReturn(mQsHeader);
         when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN);
@@ -752,7 +768,8 @@
                 mPowerInteractor,
                 mKeyguardClockPositionAlgorithm,
                 mNaturalScrollingSettingObserver,
-                mMSDLPlayer);
+                mMSDLPlayer,
+                mBrightnessMirrorShowingInteractor);
         mNotificationPanelViewController.initDependencies(
                 mCentralSurfaces,
                 null,
@@ -906,7 +923,7 @@
     }
 
     protected boolean onTouchEvent(MotionEvent ev) {
-        return mTouchHandler.onTouch(mView, ev);
+        return mNotificationPanelViewController.handleExternalTouch(ev);
     }
 
     protected void setDozing(boolean dozing, boolean dozingAlwaysOn) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index ec75972..550fcf7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -364,6 +364,64 @@
     }
 
     @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_SHADE_EXPANDS_ON_STATUS_BAR_LONG_PRESS)
+    public void onStatusBarLongPress_shadeExpands() {
+        long downTime = 42L;
+        // Start touch session with down event
+        onTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 1f, 1f, 0));
+        // Status bar triggers long press expand
+        mNotificationPanelViewController.onStatusBarLongPress(
+                MotionEvent.obtain(downTime, downTime + 27L, MotionEvent.ACTION_MOVE, 1f, 1f, 0));
+        assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
+        // Shade ignores the rest of the long press's touch session
+        assertThat(onTouchEvent(
+                MotionEvent.obtain(downTime, downTime + 42L, MotionEvent.ACTION_MOVE, 1f, 1f,
+                        0))).isFalse();
+
+        // Start new touch session
+        long downTime2 = downTime + 100L;
+        assertThat(onTouchEvent(
+                MotionEvent.obtain(downTime2, downTime2, MotionEvent.ACTION_DOWN, 1f, 1f,
+                        0))).isTrue();
+        // Shade no longer ignoring touches
+        assertThat(onTouchEvent(
+                MotionEvent.obtain(downTime2, downTime2 + 2L, MotionEvent.ACTION_MOVE, 1f, 1f,
+                        0))).isTrue();
+    }
+
+    @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_SHADE_EXPANDS_ON_STATUS_BAR_LONG_PRESS)
+    public void onStatusBarLongPress_qsExpands() {
+        long downTime = 42L;
+        // Start with shade already expanded
+        mNotificationPanelViewController.setExpandedFraction(1F);
+
+        // Start touch session with down event
+        onTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 1f, 1f, 0));
+        // Status bar triggers long press expand
+        mNotificationPanelViewController.onStatusBarLongPress(
+                MotionEvent.obtain(downTime, downTime + 27L, MotionEvent.ACTION_MOVE, 1f, 1f, 0));
+        assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
+        // Shade expands to QS
+        verify(mQsController, atLeastOnce()).flingQs(0F, ShadeViewController.FLING_EXPAND);
+        // Shade ignores the rest of the long press's touch session
+        assertThat(onTouchEvent(
+                MotionEvent.obtain(downTime, downTime + 42L, MotionEvent.ACTION_MOVE, 1f, 1f,
+                        0))).isFalse();
+
+        // Start new touch session
+        long downTime2 = downTime + 100L;
+        assertThat(onTouchEvent(
+                MotionEvent.obtain(downTime2, downTime2, MotionEvent.ACTION_DOWN, 1f, 1f,
+                        0))).isTrue();
+        // Shade no longer ignoring touches
+        assertThat(onTouchEvent(
+                MotionEvent.obtain(downTime2, downTime2 + 2L, MotionEvent.ACTION_MOVE, 1f, 1f,
+                        0))).isTrue();
+
+    }
+
+    @Test
     @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
     public void test_pulsing_onTouchEvent_noTracking() {
         // GIVEN device is pulsing
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/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 e33ce9c..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 }
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 01e5501..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
@@ -19,6 +19,7 @@
 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
@@ -53,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
@@ -62,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
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/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index d7d57c8..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
@@ -19,6 +19,7 @@
 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
@@ -51,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
@@ -60,6 +62,7 @@
 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
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/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
index 2a196c6..1b3f29a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
@@ -32,7 +32,7 @@
 import org.junit.runner.RunWith
 import org.mockito.kotlin.verify
 
-@EnableFlags(StatusBarSimpleFragment.FLAG_NAME)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CommandQueueInitializerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
index f7a8858..3b720ef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
@@ -50,7 +50,7 @@
         MockitoAnnotations.initMocks(this)
 
         testScope = TestScope()
-        underTest = RemoteInputRepositoryImpl(remoteInputManager)
+        underTest = RemoteInputRepositoryImpl(testScope.backgroundScope, remoteInputManager)
     }
 
     @Test
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/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
index 75479ad..60b95ad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification
 
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -35,6 +37,13 @@
     private var scrimOffset = 0f
     private var contentHeight = 0f
     private var isCurrentGestureOverscroll = false
+    private val customFlingBehavior =
+        object : FlingBehavior {
+            override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+                scrollBy(initialVelocity)
+                return initialVelocity / 2f
+            }
+        }
 
     private val scrollConnection =
         NotificationScrimNestedScrollConnection(
@@ -51,6 +60,7 @@
                 wasStarted = true
                 isStarted = false
             },
+            flingBehavior = customFlingBehavior,
         )
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
index 9f752a8..3b5d358 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
@@ -86,6 +87,7 @@
 
     private var bypassEnabled: Boolean = false
     private var statusBarState: Int = StatusBarState.KEYGUARD
+
     private fun eased(dozeAmount: Float) =
         notificationWakeUpCoordinator.dozeAmountInterpolator.getInterpolation(dozeAmount)
 
@@ -119,6 +121,7 @@
                 logger,
                 kosmos.notificationsKeyguardInteractor,
                 kosmos.communalInteractor,
+                kosmos.pulseExpansionInteractor,
             )
         statusBarStateCallback = withArgCaptor {
             verify(statusBarStateController).addCallback(capture())
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/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
index 133a114..dcc8ecd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
@@ -58,19 +58,4 @@
 
             assertThat(notifsFullyHidden).isTrue()
         }
-
-    @Test
-    fun isPulseExpanding_reflectsRepository() =
-        testComponent.runTest {
-            underTest.setPulseExpanding(false)
-            val isPulseExpanding by collectLastValue(underTest.isPulseExpanding)
-            runCurrent()
-
-            assertThat(isPulseExpanding).isFalse()
-
-            underTest.setPulseExpanding(true)
-            runCurrent()
-
-            assertThat(isPulseExpanding).isTrue()
-        }
 }
diff --git a/packages/SystemUI/multivalentTests/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
index a21ca94..c9ca67e 100644
--- a/packages/SystemUI/multivalentTests/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/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index c005743..657e9df 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -58,6 +58,7 @@
 import com.android.systemui.util.mockito.withArgCaptor
 import com.android.systemui.util.time.SystemClock
 import com.android.systemui.wmshell.BubblesManager
+import com.google.android.msdl.domain.MSDLPlayer
 import java.util.Optional
 import junit.framework.Assert
 import org.junit.After
@@ -110,6 +111,7 @@
     private val dismissibilityProvider: NotificationDismissibilityProvider = mock()
     private val statusBarService: IStatusBarService = mock()
     private val uiEventLogger: UiEventLogger = mock()
+    private val msdlPlayer: MSDLPlayer = mock()
     private lateinit var controller: ExpandableNotificationRowController
 
     @Before
@@ -150,7 +152,8 @@
                 dragController,
                 dismissibilityProvider,
                 statusBarService,
-                uiEventLogger
+                uiEventLogger,
+                msdlPlayer,
             )
         whenever(view.childrenContainer).thenReturn(childrenContainer)
 
@@ -268,14 +271,14 @@
         controller.mSettingsListener.onSettingChanged(
             BUBBLES_SETTING_URI,
             view.entry.sbn.userId,
-            "1"
+            "1",
         )
         verify(childView).setBubblesEnabledForUser(true)
 
         controller.mSettingsListener.onSettingChanged(
             BUBBLES_SETTING_URI,
             view.entry.sbn.userId,
-            "9"
+            "9",
         )
         verify(childView).setBubblesEnabledForUser(false)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
index 2349c25..07935e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -16,10 +16,12 @@
 
 package com.android.systemui.statusbar.notification.stack
 
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -30,12 +32,14 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 private const val MAX_PULSE_HEIGHT = 100000f
 
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @SmallTest
-class AmbientStateTest : SysuiTestCase() {
+class AmbientStateTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     private val dumpManager = mock<DumpManager>()
     private val sectionProvider = StackScrollAlgorithm.SectionProvider { _, _ -> false }
@@ -46,6 +50,18 @@
 
     private lateinit var sut: AmbientState
 
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
     @Before
     fun setUp() {
         sut =
@@ -56,7 +72,7 @@
                 bypassController,
                 statusBarKeyguardViewManager,
                 largeScreenShadeInterpolator,
-                avalancheController
+                avalancheController,
             )
     }
 
@@ -97,6 +113,7 @@
 
         assertThat(sut.pulseHeight).isEqualTo(expected)
     }
+
     // endregion
 
     // region statusBarState
@@ -119,6 +136,7 @@
 
         assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
     }
+
     // endregion
 
     // region hideAmount
@@ -141,6 +159,7 @@
 
         assertThat(sut.pulseHeight).isEqualTo(1f)
     }
+
     // endregion
 
     // region dozeAmount
@@ -173,6 +192,7 @@
 
         assertThat(sut.pulseHeight).isEqualTo(1f)
     }
+
     // endregion
 
     // region trackedHeadsUpRow
@@ -189,10 +209,12 @@
 
         assertThat(sut.trackedHeadsUpRow).isNull()
     }
+
     // endregion
 
     // region isSwipingUp
     @Test
+    @DisableSceneContainer
     fun isSwipingUp_whenValueChangedToTrue_shouldRequireFling() {
         sut.isSwipingUp = false
         sut.isFlingRequiredAfterLockScreenSwipeUp = false
@@ -203,6 +225,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun isSwipingUp_whenValueChangedToFalse_shouldRequireFling() {
         sut.isSwipingUp = true
         sut.isFlingRequiredAfterLockScreenSwipeUp = false
@@ -211,10 +234,12 @@
 
         assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
     }
+
     // endregion
 
     // region isFlinging
     @Test
+    @DisableSceneContainer
     fun isFlinging_shouldNotNeedFling() {
         sut.arrangeFlinging(true)
 
@@ -224,6 +249,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun isFlinging_whenNotOnLockScreen_shouldDoNothing() {
         sut.arrangeFlinging(true)
         sut.setStatusBarState(StatusBarState.SHADE)
@@ -235,6 +261,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun isFlinging_whenValueChangedToTrue_shouldDoNothing() {
         sut.arrangeFlinging(false)
 
@@ -242,10 +269,12 @@
 
         assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
     }
+
     // endregion
 
     // region scrollY
     @Test
+    @DisableSceneContainer
     fun scrollY_shouldSetValueGreaterThanZero() {
         sut.scrollY = 0
 
@@ -255,6 +284,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun scrollY_shouldNotSetValueLessThanZero() {
         sut.scrollY = 0
 
@@ -262,21 +292,24 @@
 
         assertThat(sut.scrollY).isEqualTo(0)
     }
+
     // endregion
 
     // region setOverScrollAmount
+    @Test
+    @DisableSceneContainer
     fun setOverScrollAmount_shouldSetValueOnTop() {
-        sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ true)
+        sut.setOverScrollAmount(/* amount= */ 10f, /* onTop= */ true)
 
-        val resultOnTop = sut.getOverScrollAmount(/* top = */ true)
-        val resultOnBottom = sut.getOverScrollAmount(/* top = */ false)
+        val resultOnTop = sut.getOverScrollAmount(/* top= */ true)
+        val resultOnBottom = sut.getOverScrollAmount(/* top= */ false)
 
         assertThat(resultOnTop).isEqualTo(10f)
         assertThat(resultOnBottom).isEqualTo(0f)
     }
 
     fun setOverScrollAmount_shouldSetValueOnBottom() {
-        sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ false)
+        sut.setOverScrollAmount(/* amount= */ 10f, /* onTop= */ false)
 
         val resultOnTop = sut.getOverScrollAmount(/* top */ true)
         val resultOnBottom = sut.getOverScrollAmount(/* top */ false)
@@ -284,6 +317,7 @@
         assertThat(resultOnTop).isEqualTo(0f)
         assertThat(resultOnBottom).isEqualTo(10f)
     }
+
     // endregion
 
     // region IsPulseExpanding
@@ -317,6 +351,7 @@
 
         assertThat(sut.isPulseExpanding).isFalse()
     }
+
     // endregion
 
     // region isOnKeyguard
@@ -333,6 +368,7 @@
 
         assertThat(sut.isOnKeyguard).isFalse()
     }
+
     // endregion
 
     // region mIsClosing
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 1ef4007..b2a485c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -4,8 +4,8 @@
 import android.content.pm.PackageManager
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import android.widget.FrameLayout
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
 import com.android.systemui.Flags
@@ -16,7 +16,9 @@
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.StatusBarState
@@ -47,10 +49,12 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class StackScrollAlgorithmTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     @JvmField @Rule var expect: Expect = Expect.create()
 
@@ -99,6 +103,18 @@
     private val bigGap = notifSectionDividerGap
     private val smallGap = px(R.dimen.notification_section_divider_height_lockscreen)
 
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
     @Before
     fun setUp() {
         Assume.assumeFalse(isTv())
@@ -614,7 +630,7 @@
 
     @Test
     fun resetViewStates_isOnKeyguard_viewBecomesTransparent() {
-        ambientState.setStatusBarState(StatusBarState.KEYGUARD)
+        ambientState.fakeShowingStackOnLockscreen()
         ambientState.hideAmount = 0.25f
         whenever(notificationRow.isHeadsUpState).thenReturn(true)
 
@@ -676,7 +692,7 @@
         whenever(row1.isHeadsUpState).thenReturn(true)
         whenever(row2.isHeadsUpState).thenReturn(false)
 
-        ambientState.setStatusBarState(StatusBarState.KEYGUARD)
+        ambientState.fakeShowingStackOnLockscreen()
         ambientState.hideAmount = 0.25f
         ambientState.dozeAmount = 0.33f
         notificationShelf.viewState.hidden = true
@@ -729,6 +745,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun resetViewStates_noSpaceForFooter_footerHidden() {
         ambientState.isShadeExpanded = true
         ambientState.stackEndHeight = 0f // no space for the footer in the stack
@@ -740,6 +757,20 @@
     }
 
     @Test
+    @EnableSceneContainer
+    fun resetViewStates_noSpaceForFooter_footerHidden_withSceneContainer() {
+        ambientState.isShadeExpanded = true
+        ambientState.stackTop = 0f
+        ambientState.stackCutoff = 100f
+        val footerView = mockFooterView(height = 200) // no space for the footer in the stack
+        hostView.addView(footerView)
+
+        stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+        assertThat((footerView.viewState as FooterViewState).hideContent).isTrue()
+    }
+
+    @Test
     fun resetViewStates_clearAllInProgress_hasNonClearableRow_footerVisible() {
         whenever(notificationRow.canViewBeCleared()).thenReturn(false)
         ambientState.isClearAllInProgress = true
@@ -1552,3 +1583,19 @@
         whenever(viewState).thenReturn(ExpandableViewState())
     }
 }
+
+private fun mockFooterView(height: Int): FooterView {
+    return mock(FooterView::class.java).apply {
+        whenever(viewState).thenReturn(FooterViewState())
+        whenever(intrinsicHeight).thenReturn(height)
+    }
+}
+
+private fun AmbientState.fakeShowingStackOnLockscreen() {
+    if (SceneContainerFlag.isEnabled) {
+        isShowingStackOnLockscreen = true
+        lockscreenStackFadeInProgress = 1f // stack is fully opaque
+    } else {
+        setStatusBarState(StatusBarState.KEYGUARD)
+    }
+}
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 d5a7c89..a940ed4 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
@@ -19,6 +19,7 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
@@ -66,10 +67,13 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.mockLargeScreenHeaderHelper
 import com.android.systemui.shade.shadeTestUtil
+import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertIs
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
@@ -165,6 +169,83 @@
         }
 
     @Test
+    fun validateHorizontalPositionSingleShade() =
+        testScope.runTest {
+            overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
+            val dimens by collectLastValue(underTest.configurationBasedDimensions)
+            shadeTestUtil.setSplitShade(false)
+
+            val horizontalPosition = checkNotNull(dimens).horizontalPosition
+            assertIs<HorizontalPosition.EdgeToEdge>(horizontalPosition)
+        }
+
+    @Test
+    fun validateHorizontalPositionSplitShade() =
+        testScope.runTest {
+            overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
+            val dimens by collectLastValue(underTest.configurationBasedDimensions)
+            shadeTestUtil.setSplitShade(true)
+
+            val horizontalPosition = checkNotNull(dimens).horizontalPosition
+            assertIs<HorizontalPosition.MiddleToEdge>(horizontalPosition)
+            assertThat(horizontalPosition.ratio).isEqualTo(0.5f)
+        }
+
+    @Test
+    @EnableSceneContainer
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun validateHorizontalPositionInSceneContainerSingleShade() =
+        testScope.runTest {
+            overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
+            val dimens by collectLastValue(underTest.configurationBasedDimensions)
+            shadeTestUtil.setSplitShade(false)
+
+            val horizontalPosition = checkNotNull(dimens).horizontalPosition
+            assertIs<HorizontalPosition.EdgeToEdge>(horizontalPosition)
+        }
+
+    @Test
+    @EnableSceneContainer
+    @DisableFlags(DualShade.FLAG_NAME)
+    fun validateHorizontalPositionInSceneContainerSplitShade() =
+        testScope.runTest {
+            overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
+            val dimens by collectLastValue(underTest.configurationBasedDimensions)
+            shadeTestUtil.setSplitShade(true)
+
+            val horizontalPosition = checkNotNull(dimens).horizontalPosition
+            assertIs<HorizontalPosition.MiddleToEdge>(horizontalPosition)
+            assertThat(horizontalPosition.ratio).isEqualTo(0.5f)
+        }
+
+    @Test
+    @EnableSceneContainer
+    @EnableFlags(DualShade.FLAG_NAME)
+    fun validateHorizontalPositionInDualShade_narrowLayout() =
+        testScope.runTest {
+            overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
+            val dimens by collectLastValue(underTest.configurationBasedDimensions)
+            shadeTestUtil.setSplitShade(false)
+
+            val horizontalPosition = checkNotNull(dimens).horizontalPosition
+            assertIs<HorizontalPosition.EdgeToEdge>(horizontalPosition)
+        }
+
+    @Test
+    @EnableSceneContainer
+    @EnableFlags(DualShade.FLAG_NAME)
+    fun validateHorizontalPositionInDualShade_wideLayout() =
+        testScope.runTest {
+            overrideDimensionPixelSize(R.dimen.shade_panel_width, 200)
+            val dimens by collectLastValue(underTest.configurationBasedDimensions)
+            shadeTestUtil.setSplitShade(true)
+
+            val horizontalPosition = checkNotNull(dimens).horizontalPosition
+            assertIs<HorizontalPosition.FloatAtEnd>(horizontalPosition)
+            assertThat(horizontalPosition.width).isEqualTo(200)
+        }
+
+    @Test
     fun validatePaddingTopInSplitShade_usesLargeHeaderHelper() =
         testScope.runTest {
             whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index 5052a00..198821f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -44,6 +44,8 @@
 import com.android.systemui.doze.DozeScreenState;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -59,6 +61,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
@@ -88,6 +91,8 @@
     @Mock private ConfigurationController mConfigurationController;
     @Mock private UserTracker mUserTracker;
     @Mock private DozeInteractor mDozeInteractor;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     @Captor private ArgumentCaptor<BatteryStateChangeCallback> mBatteryStateChangeCallback;
 
     /**
@@ -134,6 +139,7 @@
             mStatusBarStateController,
             mUserTracker,
             mDozeInteractor,
+            mKeyguardTransitionInteractor,
             secureSettings
         );
 
@@ -286,6 +292,18 @@
         assertTrue(mDozeParameters.shouldControlScreenOff());
     }
 
+    @Test
+    public void shouldDelayDisplayDozeTransition_True_WhenTransitioningToAod() {
+        setShouldControlUnlockedScreenOffForTest(false);
+        when(mScreenOffAnimationController.shouldDelayDisplayDozeTransition()).thenReturn(false);
+        when(mKeyguardTransitionInteractor.getTransitionState().getValue().getTo())
+                .thenReturn(KeyguardState.LOCKSCREEN);
+        assertFalse(mDozeParameters.shouldDelayDisplayDozeTransition());
+
+        when(mKeyguardTransitionInteractor.getTransitionState().getValue().getTo())
+                .thenReturn(KeyguardState.AOD);
+        assertTrue(mDozeParameters.shouldDelayDisplayDozeTransition());
+    }
 
     @Test
     public void keyguardVisibility_changesControlScreenOffAnimation() {
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..0eb6203 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;
@@ -800,6 +804,13 @@
     }
 
     @Test
+    public void onBackPressedResetsLeaveOnKeyguardHide() {
+        when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
+        mStatusBarKeyguardViewManager.onBackPressed();
+        verify(mStatusBarStateController).setLeaveOpenOnKeyguardHide(false);
+    }
+
+    @Test
     public void testResetHideBouncerWhenShowingIsFalse_alternateBouncerHides() {
         // GIVEN the keyguard is showing
         reset(mAlternateBouncerInteractor);
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/phone/StatusBarTouchableRegionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
index 48c2cc7..a008588 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
@@ -20,6 +20,8 @@
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.kosmos.testScope
@@ -113,4 +115,21 @@
 
             assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
         }
+
+    @Test
+    @DisableSceneContainer
+    fun entireScreenTouchable_communalVisible() =
+        testScope.runTest {
+            assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
+
+            kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Communal)
+            runCurrent()
+
+            assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue()
+
+            kosmos.fakeCommunalSceneRepository.snapToScene(CommunalScenes.Blank)
+            runCurrent()
+
+            assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt
new file mode 100644
index 0000000..c90183d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/KeyguardBypassInteractorTest.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.phone.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.configureKeyguardBypass
+import com.android.systemui.keyguard.domain.interactor.KeyguardBypassInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardBypassInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.shadeTestUtil
+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.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
+class KeyguardBypassInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private lateinit var underTest: KeyguardBypassInteractor
+
+    @Test
+    fun canBypassFalseWhenBypassAvailableFalse() =
+        testScope.runTest {
+            initializeDependenciesForCanBypass(skipIsBypassAvailableCheck = false)
+            val canBypass by collectLastValue(underTest.canBypass)
+            runCurrent()
+            assertThat(canBypass).isFalse()
+        }
+
+    @Test
+    fun canBypassTrueOnPrimaryBouncerShowing() =
+        testScope.runTest {
+            initializeDependenciesForCanBypass(skipBouncerShowingCheck = false)
+            val canBypass by collectLastValue(underTest.canBypass)
+            runCurrent()
+            assertThat(canBypass).isTrue()
+        }
+
+    @Test
+    fun canBypassTrueOnAlternateBouncerShowing() =
+        testScope.runTest {
+            initializeDependenciesForCanBypass(skipAlternateBouncerShowingCheck = false)
+            val canBypass by collectLastValue(underTest.canBypass)
+            runCurrent()
+            assertThat(canBypass).isTrue()
+        }
+
+    @Test
+    fun canBypassFalseWhenNotOnLockscreenScene() =
+        testScope.runTest {
+            initializeDependenciesForCanBypass(skipOnLockscreenSceneCheck = false)
+            val canBypass by collectLastValue(underTest.canBypass)
+            val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+            runCurrent()
+            assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen)
+            assertThat(canBypass).isFalse()
+        }
+
+    @Test
+    fun canBypassFalseOnLaunchingAffordance() =
+        testScope.runTest {
+            initializeDependenciesForCanBypass(skipLaunchingAffordanceCheck = false)
+            val canBypass by collectLastValue(underTest.canBypass)
+            runCurrent()
+            assertThat(canBypass).isFalse()
+        }
+
+    @Test
+    fun canBypassFalseOnPulseExpanding() =
+        testScope.runTest {
+            initializeDependenciesForCanBypass(skipPulseExpandingCheck = false)
+            val canBypass by collectLastValue(underTest.canBypass)
+            runCurrent()
+            assertThat(canBypass).isFalse()
+        }
+
+    @Test
+    fun canBypassFalseOnQsExpanded() =
+        testScope.runTest {
+            initializeDependenciesForCanBypass(skipQsExpandedCheck = false)
+            val canBypass by collectLastValue(underTest.canBypass)
+            runCurrent()
+            assertThat(canBypass).isFalse()
+        }
+
+    // Initializes all canBypass dependencies to opposite of value needed to return
+    private fun initializeDependenciesForCanBypass(
+        skipIsBypassAvailableCheck: Boolean = true,
+        skipBouncerShowingCheck: Boolean = true,
+        skipAlternateBouncerShowingCheck: Boolean = true,
+        skipOnLockscreenSceneCheck: Boolean = true,
+        skipLaunchingAffordanceCheck: Boolean = true,
+        skipPulseExpandingCheck: Boolean = true,
+        skipQsExpandedCheck: Boolean = true,
+    ) {
+        // !isBypassAvailable false
+        kosmos.configureKeyguardBypass(isBypassAvailable = skipIsBypassAvailableCheck)
+        underTest = kosmos.keyguardBypassInteractor
+
+        // bouncerShowing false, !onLockscreenScene false
+        // !onLockscreenScene false
+        setScene(
+            bouncerShowing = !skipBouncerShowingCheck,
+            onLockscreenScene = skipOnLockscreenSceneCheck,
+        )
+        // alternateBouncerShowing false
+        setAlternateBouncerShowing(!skipAlternateBouncerShowingCheck)
+        // launchingAffordance false
+        setLaunchingAffordance(!skipLaunchingAffordanceCheck)
+        // pulseExpanding false
+        setPulseExpanding(!skipPulseExpandingCheck)
+        // qsExpanding false
+        setQsExpanded(!skipQsExpandedCheck)
+    }
+
+    private fun setAlternateBouncerShowing(alternateBouncerVisible: Boolean) {
+        kosmos.keyguardBouncerRepository.setAlternateVisible(alternateBouncerVisible)
+    }
+
+    private fun setScene(bouncerShowing: Boolean, onLockscreenScene: Boolean) {
+        if (bouncerShowing) {
+            kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "reason")
+        } else if (onLockscreenScene) {
+            kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+        } else {
+            kosmos.sceneInteractor.changeScene(Scenes.Shade, "reason")
+        }
+    }
+
+    private fun setLaunchingAffordance(launchingAffordance: Boolean) {
+        kosmos.keyguardQuickAffordanceInteractor.setLaunchingAffordance(launchingAffordance)
+    }
+
+    private fun setPulseExpanding(pulseExpanding: Boolean) {
+        kosmos.pulseExpansionInteractor.setPulseExpanding(pulseExpanding)
+    }
+
+    private fun setQsExpanded(qsExpanded: Boolean) {
+        if (qsExpanded) {
+            kosmos.shadeTestUtil.setQsExpansion(1f)
+        } else {
+            kosmos.shadeTestUtil.setQsExpansion(0f)
+        }
+    }
+}
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/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/util/wakelock/WakeLockTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index 207c35d..90aecfb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -21,43 +21,23 @@
 
 import android.os.Build;
 import android.os.PowerManager;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
-import org.junit.Rule;
 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;
-
 
 @SmallTest
-@RunWith(ParameterizedAndroidJunit4.class)
+@RunWith(AndroidJUnit4.class)
 public class WakeLockTest extends SysuiTestCase {
 
-    @Parameters(name = "{0}")
-    public static List<FlagsParameterization> getFlags() {
-        return FlagsParameterization.allCombinationsOf(
-                Flags.FLAG_DELAYED_WAKELOCK_RELEASE_ON_BACKGROUND_THREAD);
-    }
-
-    @Rule public final SetFlagsRule mSetFlagsRule;
-
-    public WakeLockTest(FlagsParameterization flags) {
-        mSetFlagsRule = new SetFlagsRule(SetFlagsRule.DefaultInitValueType.NULL_DEFAULT, flags);
-    }
-
     private static final String WHY = "test";
     WakeLock mWakeLock;
     PowerManager.WakeLock mInner;
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/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/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index e264264..5792175 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
@@ -203,12 +203,12 @@
     fun onZenDataChanged(data: ZenData)
 
     /** Update reactive axes for this clock */
-    fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>)
+    fun onFontAxesChanged(axes: List<ClockFontAxisSetting>)
 }
 
 /** Axis setting value for a clock */
-data class ClockReactiveSetting(
-    /** Axis key; matches ClockReactiveAxis.key */
+data class ClockFontAxisSetting(
+    /** Axis key; matches ClockFontAxis.key */
     val key: String,
 
     /** Value to set this axis to */
@@ -323,11 +323,11 @@
     val isReactiveToTone: Boolean = true,
 
     /** Font axes that can be modified on this clock */
-    val axes: List<ClockReactiveAxis> = listOf(),
+    val axes: List<ClockFontAxis> = listOf(),
 )
 
 /** Represents an Axis that can be modified */
-data class ClockReactiveAxis(
+data class ClockFontAxis(
     /** Axis key, not user renderable */
     val key: String,
 
@@ -348,15 +348,17 @@
 
     /** Description of the axis */
     val description: String,
-)
+) {
+    fun toSetting() = ClockFontAxisSetting(key, currentValue)
+}
 
 /** Axis user interaction modes */
 enum class AxisType {
-    /** Boolean toggle. Swaps between minValue & maxValue */
-    Toggle,
+    /** Continuous range between minValue & maxValue. */
+    Float,
 
-    /** Continuous slider between minValue & maxValue */
-    Slider,
+    /** Only minValue & maxValue are valid. No intermediate values between them are allowed. */
+    Boolean,
 }
 
 /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
@@ -404,7 +406,7 @@
 data class ClockSettings(
     val clockId: ClockId? = null,
     val seedColor: Int? = null,
-    val axes: List<ClockReactiveSetting>? = null,
+    val axes: List<ClockFontAxisSetting>? = null,
 ) {
     // Exclude metadata from equality checks
     var metadata: JSONObject = JSONObject()
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background_small_radius.xml b/packages/SystemUI/res/drawable/volume_dialog_background_small_radius.xml
new file mode 100644
index 0000000..7d79496
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_dialog_background_small_radius.xml
@@ -0,0 +1,21 @@
+<?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">
+    <corners android:radius="@dimen/volume_dialog_background_square_corner_radius" />
+    <solid android:color="?androidprv:attr/materialColorSurface" />
+</shape>
\ 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 df8521a..131201e 100644
--- a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
@@ -17,8 +17,8 @@
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:paddingMode="stack" >
     <size
-        android:height="@dimen/volume_ringer_item_size"
-        android:width="@dimen/volume_ringer_item_size" />
+        android:height="@dimen/volume_dialog_ringer_drawer_button_size"
+        android:width="@dimen/volume_dialog_ringer_drawer_button_size" />
     <solid android:color="?androidprv:attr/materialColorPrimary" />
-    <corners android:radius="360dp" />
+    <corners android:radius="@dimen/volume_dialog_ringer_selected_button_background_radius" />
 </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
index 7ddd57b7..a8c9818 100644
--- a/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
+++ b/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
@@ -16,7 +16,7 @@
 <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"/>
+    <size android:width="@dimen/volume_dialog_ringer_drawer_button_size" android:height="@dimen/volume_dialog_ringer_drawer_button_size"/>
     <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest" />
-    <corners android:radius="@dimen/volume_ringer_item_radius" />
+    <corners android:radius="@dimen/volume_dialog_background_square_corner_radius" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 8b39e5e..e90f055f 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -40,9 +40,9 @@
         android:layout_height="wrap_content"
         android:background="@drawable/volume_dialog_background"
         android:divider="@drawable/volume_dialog_spacer"
+        android:paddingVertical="@dimen/volume_dialog_vertical_padding"
         android:gravity="center_horizontal"
         android:orientation="vertical"
-        android:paddingVertical="@dimen/volume_dialog_vertical_padding"
         android:showDividers="middle">
 
         <include layout="@layout/volume_ringer_drawer" />
diff --git a/packages/SystemUI/res/layout/volume_ringer_button.xml b/packages/SystemUI/res/layout/volume_ringer_button.xml
new file mode 100644
index 0000000..dc6780a
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_ringer_button.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<FrameLayout  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content" >
+
+    <ImageButton
+        android:id="@+id/volume_drawer_button"
+        android:layout_width="@dimen/volume_dialog_ringer_drawer_button_size"
+        android:layout_height="@dimen/volume_dialog_ringer_drawer_button_size"
+        android:padding="@dimen/volume_dialog_ringer_drawer_button_icon_radius"
+        android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
+        android:contentDescription="@string/volume_ringer_mode"
+        android:gravity="center"
+        android:src="@drawable/volume_ringer_item_bg"
+        android:background="@drawable/volume_ringer_item_bg"/>
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index 94b55b1..9b3af52 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -20,9 +20,8 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:gravity="center"
-    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:paddingLeft="@dimen/volume_dialog_ringer_horizontal_padding"
+    android:paddingRight="@dimen/volume_dialog_ringer_horizontal_padding"
     android:layoutDirection="ltr"
     android:clipToPadding="false"
     android:clipChildren="false"
@@ -31,7 +30,6 @@
     <!-- 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:orientation="vertical">
@@ -40,9 +38,8 @@
         <FrameLayout
             android:id="@+id/volume_drawer_selection_background"
             android:alpha="0.0"
-            android:layout_width="@dimen/volume_ringer_item_size"
-            android:layout_marginBottom="8dp"
-            android:layout_height="@dimen/volume_ringer_item_size"
+            android:layout_width="@dimen/volume_dialog_ringer_drawer_button_size"
+            android:layout_height="@dimen/volume_dialog_ringer_drawer_button_size"
             android:layout_gravity="bottom|right"
             android:background="@drawable/volume_drawer_selection_bg" />
 
@@ -52,64 +49,7 @@
             android:layout_height="wrap_content"
             android:orientation="vertical">
 
-            <FrameLayout
-                android:id="@+id/volume_drawer_vibrate"
-                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:background="@drawable/volume_ringer_item_bg">
-
-                <ImageView
-                    android:id="@+id/volume_drawer_vibrate_icon"
-                    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="?androidprv:attr/materialColorOnSurface" />
-
-            </FrameLayout>
-
-            <FrameLayout
-                android:id="@+id/volume_drawer_mute"
-                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:background="@drawable/volume_ringer_item_bg">
-
-                <ImageView
-                    android:id="@+id/volume_drawer_mute_icon"
-                    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="?androidprv:attr/materialColorOnSurface" />
-
-            </FrameLayout>
-
-            <FrameLayout
-                android:id="@+id/volume_drawer_normal"
-                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:background="@drawable/volume_ringer_item_bg">
-
-                <ImageView
-                    android:id="@+id/volume_drawer_normal_icon"
-                    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="?androidprv:attr/materialColorOnSurface" />
-
-            </FrameLayout>
+            <!-- add ringer buttons here -->
 
         </LinearLayout>
 
@@ -117,23 +57,16 @@
 
     <!-- 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_item_size"
-        android:layout_height="@dimen/volume_ringer_item_size"
-        android:layout_marginBottom="8dp"
-        android:layout_gravity="bottom|right"
+    <ImageButton
+        android:id="@+id/volume_new_ringer_active_button"
+        android:layout_width="@dimen/volume_dialog_ringer_drawer_button_size"
+        android:layout_height="@dimen/volume_dialog_ringer_drawer_button_size"
+        android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
+        android:background="@drawable/volume_drawer_selection_bg"
         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_icon_size"
-            android:layout_height="@dimen/volume_ringer_icon_size"
-            android:layout_gravity="center"
-            android:tint="?androidprv:attr/materialColorOnPrimary"
-            android:src="@drawable/ic_volume_media" />
-
-    </FrameLayout>
+        android:gravity="center"
+        android:padding="10dp"
+        android:src="@drawable/ic_volume_media"
+        android:tint="?androidprv:attr/materialColorOnPrimary" />
 
 </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..c091cbf 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Neem jou skerm op?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Neem een app op"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Neem hele skerm op"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Wanneer jy jou hele skerm opneem, word enigiets wat op jou skerm wys, opgeneem. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Wanneer jy ’n app opneem, word enigiets wat in daardie app gewys of gespeel word, opgeneem. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Neem skerm op"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toeganklikheid"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen soekresultate nie"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..af2971b 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ማያ ገፅዎን ይቀዳሉ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"አንድ መተግበሪያ ቅዳ"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"መላው ማያ ገፅን ቅረጽ"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"መላው ማያ ገፅዎን በሚቀዱበት ጊዜ፣ በማያ ገፅዎ ላይ የሚታየው ማንኛውም ነገር ይቀዳል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"መተግበሪያን ሲቀዱ በዚያ መተግበሪያ ውስጥ የሚታይ ወይም የሚጫወት ማንኛውም ነገር ይቀዳል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ማያ ገፅን ቅረጽ"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ምንም የፍለጋ ውጤቶች የሉም"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..4ebac5a 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"هل تريد تسجيل محتوى الشاشة؟"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"تسجيل محتوى تطبيق واحد"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"تسجيل محتوى الشاشة بالكامل"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"أثناء تسجيل محتوى الشاشة بالكامل، يتم تسجيل كل المحتوى المعروض على شاشتك. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"أثناء تسجيل محتوى تطبيق، يتم تسجيل أي محتوى يتم عرضه أو تشغيله في ذلك التطبيق. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"تسجيل محتوى الشاشة"</string>
@@ -136,11 +138,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 +523,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 +565,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"اختصارات طلبات البحث"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ما مِن نتائج بحث"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..85517f3 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপোনাৰ স্ক্ৰীনখন ৰেকৰ্ড কৰিবনে?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"এটা এপ্ ৰেকৰ্ড কৰক"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"গোটেই স্ক্ৰীনখন ৰেকৰ্ড কৰক"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"আপুনি গোটেই স্ক্ৰীনখন ৰেকৰ্ডিং কৰিলে, আপোনাৰ স্ক্ৰীনখনত দেখুওৱা যিকোনো বস্তু ৰেকৰ্ড কৰা হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"আপুনি কোনো এপ্ ৰেকৰ্ড কৰিলে, সেই এপত দেখুওৱা বা প্লে’ কৰা যিকোনো বস্তু ৰেকৰ্ড কৰা হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"স্ক্ৰীনখন ৰেকৰ্ড কৰক"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"সন্ধানৰ কোনো ফলাফল নাই"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1457,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 +1483,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..f08724a 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekran qeydə alınsın?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bir tətbiqi qeydə alın"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Bütün ekranı qeydə alın"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Bütün ekranı qeydə alarkən ekranda göstərilən bütün kontent qeydə alınır. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Tətbiq qeydə aldıqda həmin tətbiqdə göstərilən və ya işə salınan bütün kontent qeydə alınır. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranı qeydə alın"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Cari tətbiq"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Xüsusi imkanlar"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Axtarış nəticəsi yoxdur"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..f509691 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>
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite da snimite ekran?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimi jednu aplikaciju"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimi ceo ekran"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kada snimate ceo ekran, snima se sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kada snimate aplikaciju, snima 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="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimi ekran"</string>
@@ -136,11 +138,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 +523,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 +567,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 +583,8 @@
     <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>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"Podešavanja obaveštenja"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"Istorija obaveštenja"</string>
     <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>
@@ -1399,14 +1413,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelna aplikacija"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tasterske prečice"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečice pretrage"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretrage"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1457,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 +1483,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..bc0e2d1 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Запісаць экран?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Запісаць адну праграму"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Запісаць змесціва ўсяго экрана"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Пры запісе ўсяго экрана запісваецца ўсё, што паказваецца на экране. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Пры запісе праграмы запісваецца ўсё, што паказваецца або прайграецца ў гэтай праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Запісаць экран"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма вынікаў пошуку"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..2ee1f5f 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се записва ли екранът?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записване на едно приложение"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записване на целия екран"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Когато записвате целия си екран, се записва всичко, което се показва на него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Когато записвате приложение, се записва всичко, което се показва или възпроизвежда в него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Записване на екрана"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма резултати от търсенето"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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..c593210 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপনার স্ক্রিন রেকর্ড করবেন?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"একটি অ্যাপ রেকর্ড করুন"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"সম্পূর্ণ স্ক্রিন রেকর্ড করুন"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"আপনার সম্পূর্ণ স্ক্রিন রেকর্ড করার সময়, আপনার স্ক্রিনে দেখানো সব কিছু রেকর্ড করা হয়। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"আপনি কোনও অ্যাপ রেকর্ড করার সময়, সেই অ্যাপে দেখানো বা চালানো সব কিছু রেকর্ড করা হয়। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"স্ক্রিন রেকর্ড করুন"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সার্চ শর্টকাট"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"কোনও সার্চ ফলাফল নেই"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..38f4265 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Snimati ekran?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimaj jednu aplikaciju"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimaj cijeli ekran"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kada snimate cijeli ekran, snimat će se sve što se prikazuje na ekranu. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kada snimate aplikaciju, snimat će se sve što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimaj ekran"</string>
@@ -136,11 +138,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 +523,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 +583,8 @@
     <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>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"Postavke obavještenja"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"Historija obavještenja"</string>
     <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>
@@ -1399,14 +1413,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1465,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 +1483,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..bcaca5a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vols gravar la pantalla?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grava una aplicació"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grava tota la pantalla"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quan graves tota la pantalla, es grava tot el que es mostra en pantalla. 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="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quan graves una aplicació, es grava tot el que es mostra o es reprodueix en aquesta aplicació. 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="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grava la pantalla"</string>
@@ -136,11 +138,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 +523,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 +565,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicació actual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilitat"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hi ha cap resultat de la cerca"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..76ae86d 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -112,8 +112,10 @@
     <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>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
+    <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 +138,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 +523,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 +557,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuální aplikace"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Přístupnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žádné výsledky hledání"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..abcd4b8 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vil du optage din skærm?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Optag én app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Optag hele skærmen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Når du optager hele skærmen, bliver alt det, der vises på skærmen, optaget. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Når du optager en app, optages alt det, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Optag skærm"</string>
@@ -136,11 +138,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 +523,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 +583,8 @@
     <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>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"Indstillinger for notifikationer"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"Notifikationshistorik"</string>
     <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>
@@ -1399,14 +1413,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuel app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hjælpefunktioner"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Der er ingen søgeresultater"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..9799a932 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Bildschirm aufnehmen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Einzelne App aufnehmen"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gesamten Bildschirm aufnehmen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Wenn du den gesamten Bildschirm aufnimmst, ist in der Aufnahme alles zu sehen, was auf dem Bildschirm angezeigt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Wenn du eine App aufnimmst, ist in der Aufnahme alles zu sehen, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Bildschirm aufnehmen"</string>
@@ -136,11 +138,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 +523,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 +583,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 +813,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 +856,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 +865,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelle App"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Bedienungshilfen"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Keine Suchergebnisse"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1466,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 +1485,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..7200a91 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Να γίνει εγγραφή της οθόνης σας;"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Εγγραφή μίας εφαρμογής"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Εγγραφή ολόκληρης της οθόνης"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Όταν κάνετε εγγραφή ολόκληρης της οθόνη σας, καταγράφεται οτιδήποτε εμφανίζεται σε αυτήν. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Όταν κάνετε εγγραφή μιας εφαρμογής, καταγράφεται οτιδήποτε εμφανίζεται ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Εγγραφή οθόνης"</string>
@@ -136,11 +138,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 +523,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 +583,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,19 +1410,24 @@
     <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>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Συντομεύσεις πληκτρολογίου"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Κανένα αποτέλεσμα αναζήτησης"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..1ecf4f1 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..f775513 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"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>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you’re recording an app, anything shown or played in that app is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -136,11 +138,14 @@
     <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>
+    <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Sharing content"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
+    <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Stop sharing?"</string>
     <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>
+    <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"You\'re currently sharing with an app"</string>
     <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 +520,8 @@
     <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>
+    <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Decrease height"</string>
+    <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Increase height"</string>
     <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 +578,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>
@@ -1399,9 +1408,15 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current App"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index e96dd8c..1ecf4f1 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..1ecf4f1 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Current app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..77c2a63 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"¿Quieres grabar la pantalla?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabar toda la pantalla"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabes toda la pantalla, se grabará todo lo que se muestre en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cuando grabes una app, se registrará todo lo que se muestre o reproduzca en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabar pantalla"</string>
@@ -136,11 +138,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 +523,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 +583,8 @@
     <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>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"Configuración de notificaciones"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"Historial de notificaciones"</string>
     <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>
@@ -1399,14 +1413,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App actual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"La búsqueda no arrojó resultados"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..81b1562 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"¿Grabar la pantalla?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una aplicación"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabar toda la pantalla"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabas toda la pantalla, se graba todo lo que se muestre en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cuando grabas una aplicación, se graba 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>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabar pantalla"</string>
@@ -136,11 +138,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 +523,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 +563,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 +583,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 +813,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 +871,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación en uso"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hay resultados de búsqueda"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..56039da5 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Kas salvestada ekraanikuvast video?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ühe rakenduse salvestamine"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Kogu ekraanikuva salvestamine"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kui salvestate kogu ekraani, salvestatakse kõik ekraanil kuvatud andmed. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kui salvestate rakendust, salvestatakse kõik, mida selles rakenduses näidatakse või esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekraanikuva jäädvustamine"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Praegune rakendus"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Juurdepääsetavus"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Otsingutulemused puuduvad"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1467,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 +1485,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..426d1d7 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Pantaila grabatu nahi duzu?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabatu aplikazio bat"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabatu pantaila osoa"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pantaila osoa grabatzen ari zarenean, pantailan agertzen den guztia grabatzen da. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Aplikazio bat grabatzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia grabatzen da. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabatu pantaila"</string>
@@ -136,11 +138,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 +523,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 +566,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Oraingo aplikazioa"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erabilerraztasuna"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ez dago bilaketa-emaitzarik"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..b0d23aa 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"صفحه‌نمایش ضبط شود؟"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ضبط یک برنامه"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ضبط کل صفحه‌نمایش"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"وقتی کل صفحه‌نمایش را ضبط می‌کنید، هر چیزی که در صفحه‌نمایش نشان داده شود ضبط خواهد شد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"وقتی برنامه‌ای را ضبط می‌کنید، هر چیزی که در آن برنامه نشان داده شود یا پخش شود ضبط خواهد شد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ضبط صفحه‌نمایش"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میان‌برها"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"نتیجه‌ای برای جستجو پیدا نشد"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..690228f 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Tallennetaanko näytön toimintaa?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Tallenna yhdestä sovelluksesta"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tallenna koko näyttö"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kun tallennat koko näyttöä, kaikki näytöllä näkyvä sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kun tallennat sovellusta, kaikki sovelluksessa näkyvä tai toistettu sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Tallenna näyttö"</string>
@@ -136,11 +138,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 +523,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 +583,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 +865,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Nykyinen sovellus"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Saavutettavuus"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ei hakutuloksia"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..5ea58ce 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Enregistrer votre écran?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Enregistrer l\'écran entier"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'affiche sur votre écran est enregistré. 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="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans cette appli est enregistré. 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="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
@@ -136,11 +138,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 +523,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 +566,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 +583,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 +865,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 +1261,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez des raccourcis"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..2be31b1 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Enregistrer l\'écran ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Enregistrer tout l\'écran"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'y affiche est enregistré. Faites donc attention aux éléments tels que 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="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans celle-ci est enregistré. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
@@ -136,11 +138,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 +330,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 +376,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Appli actuelle"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1459,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 +1485,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..2bd0b30 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Queres gravar a túa pantalla?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar unha aplicación"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar pantalla completa"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cando gravas a pantalla completa, recóllese todo o que se mostra nela. Recomendámosche que teñas coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cando gravas unha aplicación, recóllese todo o que se mostra ou reproduce nela. Recomendámosche que teñas coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar pantalla"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicación actual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidade"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atallos de busca"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Non hai resultados de busca"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..becca8f 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"તમારી સ્ક્રીન રેકોર્ડ કરીએ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"એક ઍપ રેકોર્ડ કરો"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"પૂર્ણ સ્ક્રીન રેકોર્ડ કરો"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"જ્યારે તમે તમારી પૂર્ણ સ્ક્રીન રેકોર્ડ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પર બતાવવામાં આવતી હોય તેવી બધી વસ્તુ રેકોર્ડ કરવામાં આવે છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"જ્યારે તમે કોઈ ઍપને રેકોર્ડ કરી રહ્યાં હો, ત્યારે એ ઍપમાં બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુ રેકોર્ડ કરવામાં આવે છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"સ્ક્રીન રેકોર્ડ કરો"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"કોઈ શોધ પરિણામો નથી"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..a6fc05e 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"क्या आपको स्क्रीन रिकॉर्ड करनी है?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक ऐप्लिकेशन की रिकॉर्डिंग करें"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूरी स्क्रीन रिकॉर्ड करें"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"पूरी स्क्रीन रिकॉर्ड करते समय, स्क्रीन पर दिखने वाली हर चीज़ रिकॉर्ड की जाती है. इसलिए पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज,  डिवाइस पर चल रहे ऑडियो और वीडियो, और फ़ोटो जैसी चीज़ों को लेकर सावधानी बरतें."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"किसी ऐप्लिकेशन को रिकॉर्ड करने के दौरान, उस पर दिख रहा कॉन्टेंट या चल रहा मीडिया दूसरी स्क्रीन पर भी रिकॉर्ड होता है. इसलिए, रिकॉर्ड करते समय पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रीन रिकॉर्ड करें"</string>
@@ -136,11 +138,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 +523,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 +565,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 +583,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 +864,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>
@@ -1399,18 +1415,23 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"सर्च शॉर्टकट"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"खोज का कोई नतीजा नहीं मिला"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1467,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 +1485,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..cb7a193 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite li snimati zaslon?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimanje jedne aplikacije"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimanje cijelog zaslona"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kad snimate cijeli zaslon, snima se sve što se prikazuje na zaslonu. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kad snimate aplikaciju, snima se sve što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimanje zaslona"</string>
@@ -136,11 +138,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 +523,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 +567,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 +583,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 +860,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutačna aplikacija"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..b09947f 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rögzíti a képernyőt?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Egyetlen alkalmazás rögzítése"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Teljes képernyő rögzítése"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"A teljes képernyő rögzítése esetén a képernyőn megjelenő minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Alkalmazás rögzítésekor az adott alkalmazásban megjelenített vagy lejátszott minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Képernyő rögzítése"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Jelenlegi alkalmazás"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kisegítő lehetőségek"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nincsenek keresési találatok"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..174b526 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Տեսագրե՞լ ձեր էկրանը"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Տեսագրել մեկ հավելված"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Տեսագրել ամբողջ էկրանը"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Երբ դուք տեսագրում եք ամբողջ էկրանը, էկրանին ցուցադրվող ամեն ինչ տեսագրվում է։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Երբ դուք որևէ հավելված եք տեսագրում, հավելվածում ցուցադրվող կամ նվագարկվող ամեն ինչ տեսագրվում է։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Տեսագրել էկրանը"</string>
@@ -136,11 +138,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 +523,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 +583,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 +868,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Որոնման արդյունքներ չկան"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1459,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 +1485,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..e706b27 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rekam layar Anda?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekam satu aplikasi"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekam seluruh layar"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Saat Anda merekam seluruh layar, semua hal yang ditampilkan di layar akan direkam. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Jika Anda merekam aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan direkam. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekam layar"</string>
@@ -136,11 +138,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>
@@ -403,7 +411,7 @@
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Alat bantu dengar"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string>
-    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Terputus"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Tidak terhubung"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Alat bantu dengar"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sambungkan perangkat baru"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string>
@@ -515,6 +523,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 +583,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>
@@ -1176,7 +1192,7 @@
     <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
     <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 perangkat dipilih"</string>
     <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> perangkat dipilih"</string>
-    <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(terputus)"</string>
+    <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(tidak terhubung)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Tidak dapat beralih. Ketuk untuk mencoba lagi."</string>
     <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Hubungkan perangkat"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Buka aplikasi untuk mentransmisikan sesi ini."</string>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikasi Saat Ini"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aksesibilitas"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tidak ada hasil penelusuran"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..26729106 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Viltu taka upp skjáinn?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Taka upp eitt forrit"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Taka upp allan skjáinn"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Þegar þú tekur upp allan skjáinn verður allt sem er sýnilegt á skjánum tekið upp. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Þegar þú tekur upp forrit verður allt sem er sýnilegt eða spilað í forritinu tekið upp. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Taka upp skjá"</string>
@@ -136,11 +138,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 +523,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 +567,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 +583,8 @@
     <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>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"Tilkynningastillingar"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"Tilkynningaferill"</string>
     <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>
@@ -1399,14 +1413,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Núverandi forrit"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aðgengi"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Engar leitarniðurstöður"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..fd56c5b 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Registrare lo schermo?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Registra un\'app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Registra l\'intero schermo"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando registri l\'intero schermo, tutto ciò che viene mostrato sullo schermo viene registrato. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando registri un\'app, tutto ciò che viene mostrato o riprodotto al suo interno viene registrato. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Registra lo schermo"</string>
@@ -136,11 +138,14 @@
     <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>
+    <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"Condivisione di contenuti in corso…"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Vuoi interrompere la condivisione dello schermo?"</string>
+    <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Interrompere la condivisione?"</string>
     <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>
+    <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Al momento stai condividendo contenuti con un\'app"</string>
     <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 +520,8 @@
     <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>
+    <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Riduci altezza"</string>
+    <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Aumenta altezza"</string>
     <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 +578,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>
@@ -1399,14 +1410,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App corrente"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilità"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nessun risultato di ricerca"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1454,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 +1480,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..384c680 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"להקליט את המסך?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"הקלטה של אפליקציה אחת"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"הקלטה של כל המסך"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"כשמקליטים את כל המסך, כל מה שמופיע במסך מוקלט. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"כשמקליטים אפליקציה, כל מה שרואים או מפעילים בה מוקלט. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"הקלטת המסך"</string>
@@ -136,11 +138,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 +523,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 +583,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>
+    <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_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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"אין תוצאות חיפוש"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1464,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 +1483,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..a0a3b2b 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"画面を録画しますか?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"1 つのアプリを録画"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"画面全体を録画"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"画面全体を録画すると、画面に表示されるものがすべて録画されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"アプリを録画すると、そのアプリで表示または再生される内容がすべて録画されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"画面を録画"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"検索結果がありません"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1457,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 +1483,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..471957e 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"გსურთ თქვენი ეკრანის ჩაწერა?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ერთი აპის ჩაწერა"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"მთლიანი ეკრანის ჩაწერა"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"მთლიანი ეკრანის ჩაწერისას ჩაიწერება ყველაფერი, რაც თქვენს ეკრანზე გამოჩნდება. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"აპის ჩაწერისას ჩაიწერება ყველაფერი, რაც ამ აპში გამოჩნდება ან დაიკვრება. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ეკრანის ჩაწერა"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ძიების შედეგები არ არის"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..3dbfd86 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Қолданба экранын жазасыз ба?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бір қолданба экранын жазу"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүкіл экранды жазу"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Бүкіл экранды жазған кезде, онда көрінетін барлық нәрсе жазылады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Қолданбаны жазған кезде, онда көрінетін не ойнатылатын барлық нәрсе жазылады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Экранды жазу"</string>
@@ -136,11 +138,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 +523,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 +583,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 +868,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Іздеу нәтижелері жоқ."</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..b2c8977 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ថត​អេក្រង់​របស់អ្នកឬ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ថត​កម្មវិធី​ទោល"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ថតអេក្រង់ទាំងមូល"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"នៅពេល​អ្នកកំពុង​ថតអេក្រង់​ទាំងមូល​របស់អ្នក អ្វីគ្រប់យ៉ាង​ដែលបង្ហាញ​នៅលើ​អេក្រង់​របស់អ្នក​ត្រូវបាន​ថត។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"នៅពេលអ្នក​កំពុង​ថតកម្មវិធី​ណាមួយ អ្វីគ្រប់យ៉ាង​ដែលបង្ហាញ ឬចាក់​នៅក្នុង​កម្មវិធីនោះ​ត្រូវបាន​ថត។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ថត​អេក្រង់"</string>
@@ -136,11 +138,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 +523,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 +583,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 +1407,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ផ្លូវ​កាត់ការស្វែងរក"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"គ្មាន​លទ្ធផល​ស្វែងរក​ទេ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..20c3676 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬೇಕೇ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ನೀವು ರೆಕಾರ್ಡ್ ಮಾಡುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ ಮೇಲೆ ಗೋಚರಿಸುವ ಎಲ್ಲವನ್ನೂ ರೆಕಾರ್ಡ್ ಮಾಡಲಾಗುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸಿರುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡಿದ ಎಲ್ಲವನ್ನೂ ರೆಕಾರ್ಡ್ ಮಾಡಲಾಗುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ಯಾವುದೇ ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳಿಲ್ಲ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..35dc245 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"화면을 녹화하시겠습니까?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"단일 앱 녹화"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"전체 화면 녹화"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"전체 화면을 녹화하면 화면에 표시되는 모든 항목이 녹화됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"앱을 녹화하면 앱에 표시되거나 앱에서 재생되는 모든 항목이 녹화됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"화면 녹화"</string>
@@ -136,11 +138,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 +523,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 +565,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 +583,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>
@@ -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,20 +1409,25 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"검색 결과 없음"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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_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_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"키보드와 터치패드를 사용하여 이동"</string>
@@ -1464,8 +1483,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..72e86cc 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Экранды жаздырасызбы?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бир колдонмону жаздыруу"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүтүндөй экранды жаздыруу"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Бүтүндөй экранды жаздырганда, андагы нерселердин баары видеого түшүп калат. Андыктан этият болуп, сырсөздөр, төлөм ыкмалары, билдирүүлөр, сүрөттөр, аудио жана видео материалдар сыяктуу купуя нерселерди көрсөтүп албаңыз."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Колдонмону жаздырганда ал колдонмодо көрсөтүлүп же ойнотулуп жаткан нерселер жаздырылат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Экранды жаздыруу"</string>
@@ -136,11 +138,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 +523,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 +562,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 +583,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 +816,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 +871,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>
@@ -1399,17 +1415,22 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Эч нерсе табылган жок"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..f6fe3cc 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ບັນທຶກໜ້າຈໍຂອງທ່ານບໍ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ບັນທຶກແອັບດຽວ"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ບັນທຶກໝົດໜ້າຈໍ"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ເມື່ອທ່ານບັນທຶກໝົດໜ້າຈໍຂອງທ່ານ, ລະບົບຈະບັນທຶກທຸກສິ່ງທີ່ສະແດງຢູ່ໜ້າຈໍຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ເມື່ອທ່ານບັນທຶກແອັບ, ລະບົບຈະບັນທຶກທຸກສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ໃນແອັບນັ້ນ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ບັນທຶກໜ້າຈໍ"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ບໍ່ມີຜົນການຊອກຫາ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..70a0063 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Įrašyti ekraną?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Įrašyti vieną programą"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Įrašyti visą ekraną"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kai įrašote visą ekraną, įrašomas visas ekrane rodomas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kai įrašote programą, įrašomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Įrašyti ekraną"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Esama programa"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pritaikomumas"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nėra jokių paieškos rezultatų"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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..360afad 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vai ierakstīt ekrānu?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ierakstīt vienu lietotni"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ierakstīt visu ekrānu"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Ierakstot visu ekrānu, viss, kas redzams ekrānā, tiek ierakstīts. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Ierakstot lietotni, tiek ierakstīts viss attiecīgajā lietotnē rādītais vai atskaņotais. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ierakstīt ekrānu"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Pašreizējā lietotne"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pieejamība"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas saīsnes"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nav meklēšanas rezultātu"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..ebd62a9 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се снима екранот?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Снимање на една апликација"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Снимање на целиот екран"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Додека го снимате целиот екран, сѐ што е прикажано на екранот се снима. Затоа, бидете внимателни со лозинките, деталите за плаќање, пораките, фотографиите и аудиото и видеото."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Додека снимате апликација, може да се сними сѐ што се прикажува или пушта во таа апликација. Затоа, бидете внимателни со лозинките, деталите за плаќање, пораките, фотографиите и аудиото и видеото."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Снимај го екранот"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Кратенки за пребарување"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултати од пребарување"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..6152fce 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"നിങ്ങളുടെ സ്ക്രീൻ റെക്കോർഡ് ചെയ്യണോ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ഒരു ആപ്പ് റെക്കോർഡ് ചെയ്യുക"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"സ്ക്രീൻ പൂർണ്ണമായി റെക്കോർഡ് ചെയ്യുക"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"നിങ്ങളുടെ സ്ക്രീൻ പൂർണ്ണമായി റെക്കോർഡ് ചെയ്യുമ്പോൾ, സ്ക്രീനിൽ ദൃശ്യമാകുന്ന എല്ലാം റെക്കോർഡ് ചെയ്യപ്പെടും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"നിങ്ങളുടെ ആപ്പ് റെക്കോർഡ് ചെയ്യുമ്പോൾ, ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാ കാര്യങ്ങളും റെക്കോർഡ് ചെയ്യപ്പെടും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"സ്ക്രീൻ റെക്കോർഡ് ചെയ്യുക"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"തിരയൽ ഫലങ്ങളൊന്നുമില്ല"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..aafc8c61 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Дэлгэцээ бичих үү?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Нэг аппыг бичих"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүтэн дэлгэцийг бичих"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Таныг бүтэн дэлгэцээ бичиж байхад дэлгэц дээр тань харуулж буй аливаа зүйлийг бичдэг. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Таныг апп бичиж байхад тухайн аппад харуулж эсвэл тоглуулж буй аливаа зүйлийг бичдэг. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Дэлгэцийг бичих"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ямар ч хайлтын илэрц байхгүй"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..fbbb167 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तुमची स्क्रीन रेकॉर्ड करायची आहे का?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक अ‍ॅप रेकॉर्ड करा"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूर्ण स्क्रीन रेकॉर्ड करा"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तुम्ही तुमची पूर्ण स्क्रीन रेकॉर्ड करता, तेव्हा तुमच्या स्क्रीनवर दाखवलेली कोणतीही गोष्टी रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"तुम्ही अ‍ॅप रेकॉर्ड करता, तेव्हा त्या अ‍ॅपमध्ये दाखवलेली किंवा प्ले केलेली कोणतीही गोष्ट रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रीन रेकॉर्ड करा"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कोणतेही शोध परिणाम नाहीत"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..087c8a6 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rakam skrin anda?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rakam satu apl"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rakam seluruh skrin"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Apabila anda merakam seluruh skrin anda, apa-apa sahaja yang dipaparkan pada skrin anda akan dirakam. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Apabila anda merakam apl, apa-apa sahaja yang dipaparkan atau dimainkan dalam apl tersebut akan dirakam. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rakam skrin"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Apl Semasa"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kebolehaksesan"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan papan kekunci"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tiada hasil carian"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..d456bca 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ဖန်သားပြင်ကို ရိုက်ကူးမလား။"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"အက်ပ်တစ်ခုကို ရိုက်ကူးရန်"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ဖန်သားပြင်တစ်ခုလုံးကို ရိုက်ကူးရန်"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"သင့်ဖန်သားပြင်တစ်ခုလုံး ရိုက်ကူးနေချိန်တွင် ဖန်သားပြင်တွင် ပြထားသည့် အရာအားလုံးကို ရိုက်ကူးသည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"အက်ပ်ကို ရိုက်ကူးနေချိန်တွင် ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို ရိုက်ကူးသည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ဖန်သားပြင်ကို ရိုက်ကူးရန်"</string>
@@ -136,11 +138,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 +523,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 +583,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 +813,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ရှာဖွေမှုရလဒ် မရှိပါ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..1fbb35b0 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vil du ta opp skjermen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ta opp én app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ta opp hele skjermen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Når du tar opp hele skjermen, blir alt som vises på skjermen, tatt opp. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Når du tar opp en app, blir alt som vises eller spilles av i appen, tatt opp. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ta opp skjermen"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktiv app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tilgjengelighet"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ingen søkeresultater"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..381118a 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तपाईंको स्क्रिन रेकर्ड गर्ने हो?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एउटा एप रेकर्ड गर्नुहोस्"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूरै स्क्रिन रेकर्ड गर्नुहोस्"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तपाईंले आफ्नो पूरै स्क्रिन रेकर्ड गरिरहेका बेला तपाईंको स्क्रिनमा देखाइने सबै सामग्री रेकर्ड गरिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"तपाईंले यो एप रेकर्ड गरिरहेका बेला यो एपमा देखाइने वा प्ले गरिने सबै सामग्री रेकर्ड गरिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रिन रेकर्ड गर्नुहोस्"</string>
@@ -136,11 +138,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 +523,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 +583,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 +816,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 +855,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कुनै पनि खोज परिणाम भेटिएन"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1459,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 +1485,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..8ff59ee 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Je scherm opnemen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Eén app opnemen"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Hele scherm opnemen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Als je je hele scherm opneemt, wordt alles opgenomen wat op je scherm wordt getoond. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Als je een app opneemt, wordt alles opgenomen wat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Scherm opnemen"</string>
@@ -136,11 +138,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 +523,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 +583,8 @@
     <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>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"Instellingen voor meldingen"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"Meldings­geschiedenis"</string>
     <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>
@@ -1399,14 +1413,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Huidige app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toegankelijkheid"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Sneltoetsen"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snelkoppelingen voor zoekopdrachten"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen zoekresultaten"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..6086440 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ରେକର୍ଡ କରିବେ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ଗୋଟିଏ ଆପ ରେକର୍ଡ କରନ୍ତୁ"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ରେକର୍ଡ କରନ୍ତୁ"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ଆପଣ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ରେକର୍ଡ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା ସବୁକିଛି ରେକର୍ଡ ହୋଇଥାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ଆପଣ ଏକ ଆପ ରେକର୍ଡ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି ରେକର୍ଡ ହୋଇଥାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ସ୍କ୍ରିନ ରେକର୍ଡ କରନ୍ତୁ"</string>
@@ -136,11 +138,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 +330,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 +523,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 +567,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"କୌଣସି ସର୍ଚ୍ଚ ଫଳାଫଳ ନାହିଁ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..b99986a 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ਕੀ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨਾ ਹੈ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ਇੱਕ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ਜਦੋਂ ਤੁਸੀਂ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਾਈ ਜਾ ਰਹੀ ਹਰ ਚੀਜ਼ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਨਾਲ ਹੀ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਉਸ ਐਪ ਵਿੱਚ ਦਿਖਾਈ ਜਾਂ ਚਲਾਈ ਜਾ ਰਹੀ ਹਰ ਚੀਜ਼ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਕਰੋ"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਖੋਜ ਸੰਬੰਧੀ ਸ਼ਾਰਟਕੱਟ"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ਕੋਈ ਖੋਜ ਨਤੀਜਾ ਨਹੀਂ ਮਿਲਿਆ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..88177ac 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Nagrywać ekran?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nagrywaj jedną aplikację"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nagrywaj cały ekran"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kiedy nagrywasz cały ekran, nagrane zostanie wszystko, co jest na nim widoczne. Dlatego uważaj na hasła, dane do płatności, wiadomości, zdjęcia, nagrania audio czy filmy."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kiedy nagrywasz aplikację, wszystko, co jest w niej wyświetlane lub odtwarzane, zostaje nagrane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nagrywaj ekran"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Bieżąca aplikacja"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ułatwienia dostępu"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Skróty klawiszowe"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Brak wyników wyszukiwania"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1459,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 +1467,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 +1485,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..e67c168 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar a tela?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar a tela toda"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando você grava a tela toda, tudo o que aparece nela é registrado. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando você grava um app, todas as informações visíveis ou abertas nele ficam registradas. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar a tela"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..30e16f0 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar o ecrã?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar uma app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar o ecrã inteiro"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando está a gravar o ecrã inteiro, tudo o que é apresentado no ecrã é gravado. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando está a gravar uma app, tudo o que é apresentado ou reproduzido nessa app é gravado. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar ecrã"</string>
@@ -136,11 +138,14 @@
     <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>
+    <string name="share_to_app_chip_accessibility_label_generic" msgid="5517431657924536133">"A partilhar conteúdo"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Parar a partilha do ecrã?"</string>
+    <string name="share_to_app_stop_dialog_title_generic" msgid="9079161538135843648">"Parar a partilha?"</string>
     <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>
+    <string name="share_to_app_stop_dialog_message_generic" msgid="7622174291691249392">"Neste momento, está a partilhar com uma app"</string>
     <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 +520,8 @@
     <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>
+    <string name="accessibility_action_label_shrink_widget" msgid="8259511040536438771">"Diminuir altura"</string>
+    <string name="accessibility_action_label_expand_widget" msgid="9190524260912211759">"Aumentar altura"</string>
     <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 +578,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>
@@ -1399,14 +1410,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos de teclado"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado da pesquisa"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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..e67c168 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar a tela?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar a tela toda"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando você grava a tela toda, tudo o que aparece nela é registrado. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando você grava um app, todas as informações visíveis ou abertas nele ficam registradas. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar a tela"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"App atual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..de7f8da 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Înregistrezi ecranul?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Înregistrează o aplicație"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Înregistrează tot ecranul"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Când înregistrezi întregul ecran, se înregistrează tot ce apare pe ecran. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Când înregistrezi o aplicație, se înregistrează tot ce se afișează sau se redă în aplicație. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Înregistrează ecranul"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplicația actuală"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilitate"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Niciun rezultat al căutării"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..a007685 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Начать запись экрана?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записывать одно приложение"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записывать весь экран"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Во время записи всего экрана все данные и действия, которые на нем показываются, попадают на видео. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Во время записи приложения все данные и действия, которые показываются в его окне, попадают на видео. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Запись экрана"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ничего не найдено"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1465,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 +1483,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..ae9c4d0 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ඔබේ තිරය පටිගත කරන්න ද?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"එක් යෙදුමක් පටිගත කරන්න"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"සම්පූර්ණ තිරය පටිගත කරන්න"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ඔබ ඔබේ සම්පූර්ණ තිරය පටිගත කරන විට, ඔබේ තිරයේ පෙන්වන ඕනෑම දෙයක් වාර්තා වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ඔබ යෙදුමක් පටිගත කරන විට, එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයක් වාර්තා වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"තිරය පටිගත කරන්න"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"සෙවීම් ප්‍රතිඵල නැත"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..c223566 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Chcete nahrávať obrazovku?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrávať jednu aplikáciu"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nahrávať celú obrazovku"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pri nahrávaní celej obrazovky sa zaznamená všetko, čo sa na nej zobrazuje. Preto venujte pozornosť položkám, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Pri nahrávaní aplikácie sa zaznamená všetko, čo sa v nej zobrazuje alebo prehráva. Preto venujte pozornosť položkám, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrávať obrazovku"</string>
@@ -136,11 +138,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 +523,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 +583,8 @@
     <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>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"Nastavenia upozornení"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"História upozornení"</string>
     <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>
@@ -1399,14 +1413,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuálna aplikácia"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostupnosť"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žiadne výsledky vyhľadávania"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..15f1b3c 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite posneti zaslon?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snemanje ene aplikacije"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snemanje celotnega zaslona"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pri snemanju celotnega zaslona se posname vse, kar je prikazano na zaslonu. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Pri snemanju aplikacije se posname vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snemanje zaslona"</string>
@@ -136,11 +138,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 +523,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 +583,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 +816,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>
@@ -1399,18 +1415,23 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostopnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Bližnjične tipke"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bližnjice za iskanje"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ni rezultatov iskanja"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..7f74487 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Të regjistrohet ekrani?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Regjistro një aplikacion"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Regjistro të gjithë ekranin"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kur regjistron të gjithë ekranin, regjistrohet çdo gjë e shfaqur në ekranin tënd. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kur regjistron një aplikacion, regjistrohet çdo gjë që shfaqet ose luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Regjistro ekranin"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikacioni aktual"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qasshmëria"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Asnjë rezultat kërkimi"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..a1952d4 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>
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Желите да снимите екран?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Сними једну апликацију"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Сними цео екран"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Када снимате цео екран, снима се све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Када снимате апликацију, снима се сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Сними екран"</string>
@@ -136,11 +138,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 +523,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 +567,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 +583,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>
@@ -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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пречице претраге"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултата претраге"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1457,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 +1483,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..b898eaf 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vill du spela in det som visas på skärmen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Spela in en app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Spela in hela skärmen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"När du spelar in hela skärmen spelas allt som visas på skärmen in. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"När du spelar in en app spelas allt som visas eller spelas upp i appen in. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Spela in skärmen"</string>
@@ -136,11 +138,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 +523,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 +583,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 +871,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuell app"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tillgänglighet"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Inga sökresultat"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..8731337 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ungependa kurekodi skrini yako?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekodi programu moja"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekodi skrini nzima"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Unaporekodi skrini yako nzima, chochote kinachoonyeshwa kwenye skrini yako kitarekodiwa. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Unaporekodi programu, chochote kinachoonyeshwa au kuchezwa kwenye programu hiyo kitarekodiwa. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekodi skrini"</string>
@@ -136,11 +138,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 +523,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 +583,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 +855,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>
@@ -1399,18 +1415,23 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Programu Inayotumika Sasa"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ufikivu"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hamna matokeo ya utafutaji"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 4007bb1..393631e 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -76,6 +76,14 @@
     <dimen name="large_dialog_width">472dp</dimen>
 
     <dimen name="large_screen_shade_header_height">42dp</dimen>
+
+    <!--
+    The horizontal distance between the shade overlay panel (both notifications and quick settings)
+    and the edge of the screen. On Compact screens in portrait orientation (< w600dp) this is
+    ignored in the shade layout, which takes up the full screen width without margins.
+    -->
+    <dimen name="shade_panel_margin_horizontal">24dp</dimen>
+
     <!-- start padding is smaller to account for status icon margins coming from drawable itself -->
     <dimen name="hover_system_icons_container_padding_start">3dp</dimen>
     <dimen name="hover_system_icons_container_padding_end">4dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml b/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
index ca62d28..f38f42d 100644
--- a/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-h1000dp/dimens.xml
@@ -23,4 +23,7 @@
     <dimen name="keyguard_clock_top_margin">80dp</dimen>
     <dimen name="bouncer_user_switcher_view_mode_user_switcher_bottom_margin">155dp</dimen>
     <dimen name="bouncer_user_switcher_view_mode_view_flipper_bottom_margin">85dp</dimen>
+
+    <!-- The width of the shade overlay panel (both notifications and quick settings). -->
+    <dimen name="shade_panel_width">474dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw800dp/dimens.xml b/packages/SystemUI/res/values-sw800dp/dimens.xml
index 0d82217..8ae3a2e 100644
--- a/packages/SystemUI/res/values-sw800dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw800dp/dimens.xml
@@ -21,4 +21,7 @@
 
     <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
     <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
+
+    <!-- The width of the shade overlay panel (both notifications and quick settings). -->
+    <dimen name="shade_panel_width">392dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index b53c8ec..b75a218 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"உங்கள் திரையை ரெக்கார்டு செய்யவா?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ஓர் ஆப்ஸை ரெக்கார்டு செய்தல்"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"முழுத் திரையை ரெக்கார்டு செய்தல்"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"முழுத் திரையை நீங்கள் ரெக்கார்டு செய்யும்போது அதில் காட்டப்படும் அனைத்தும் ரெக்கார்டு செய்யப்படும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ஓர் ஆப்ஸை ரெக்கார்டு செய்யும்போது அதில் காட்டப்படும் அல்லது பிளே செய்யப்படும் அனைத்தும் ரெக்கார்டு செய்யப்படும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"திரையை ரெக்கார்டு செய்"</string>
@@ -136,11 +138,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 +523,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 +583,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 +868,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"தேடல் ஷார்ட்கட்கள்"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"தேடல் முடிவுகள் இல்லை"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..d66c656 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"మీ స్క్రీన్‌ను రికార్డ్ చేయాలా?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ఒక యాప్‌ను రికార్డ్ చేయండి"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ఫుల్ స్క్రీన్‌ను రికార్డ్ చేయండి"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"మీ ఫుల్ స్క్రీన్‌ను మీరు రికార్డ్ చేసేటప్పుడు, మీ స్క్రీన్‌పై కనిపించేవన్నీ రికార్డ్ అవుతాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"మీరు యాప్‌ను రికార్డ్ చేసేటప్పుడు, సంబంధిత యాప్‌లో కనిపించేవన్నీ లేదా ప్లే అయ్యేవన్నీ రికార్డ్ అవుతాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"స్క్రీన్‌ను రికార్డ్ చేయండి"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -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>
@@ -1399,14 +1413,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"సెర్చ్ షార్ట్‌కట్‌లు"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"సెర్చ్ ఫలితాలు ఏవీ లేవు"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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..b7d1a32 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"บันทึกหน้าจอไหม"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"บันทึกแอปเดียว"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"บันทึกทั้งหน้าจอ"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ขณะบันทึกทั้งหน้าจอ ระบบจะบันทึกทุกสิ่งที่แสดงอยู่บนหน้าจอ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ขณะบันทึกแอป ระบบจะบันทึกทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"บันทึกหน้าจอ"</string>
@@ -136,11 +138,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 +523,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 +583,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 +816,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ไม่พบผลการค้นหา"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..a30a2f2 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"I-record ang iyong screen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Mag-record ng isang app"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"I-record ang buong screen"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kapag nire-record mo ang iyong buong screen, nire-record ang anumang ipinapakita sa screen mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kapag nagre-record ka ng app, nire-record ang anumang ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"I-record ang screen"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Kasalukuyang App"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Walang resulta ng paghahanap"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..c79dfcf 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekranınız kaydedilsin mi?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bir uygulamayı kaydet"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tüm ekranı kaydedin"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Tüm ekranınızı kaydettiğinizde ekranınızda gösterilen her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Bir uygulamayı kaydettiğinizde o uygulamada gösterilen veya oynatılan her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranı kaydet"</string>
@@ -136,11 +138,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 +523,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 +583,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 +1407,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Mevcut Uygulama"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erişilebilirlik"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Arama sonucu yok"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..2808922 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Записати відео з екрана?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записувати один додаток"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записувати весь екран"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Коли ви записуєте вміст усього екрана, на відео потрапляє все, що на ньому відображається. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Коли ви записуєте додаток, на відео потрапляє все, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Записувати вміст екрана"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,16 +1415,21 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нічого не знайдено"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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_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_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навігація за допомогою клавіатури й сенсорної панелі"</string>
@@ -1464,8 +1485,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..b72464ca 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"آپ کی اسکرین ریکارڈ کریں؟"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ایک ایپ ریکارڈ کریں"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"پوری اسکرین کو ریکارڈ کریں"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"جب آپ اپنی پوری اسکرین کو ریکارڈ کر رہے ہوتے ہیں تو آپ کی اسکرین پر دکھائی گئی ہر چیز ریکارڈ کی جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"جب آپ کسی ایپ کو ریکارڈ کر رہے ہوتے ہیں تو اس ایپ میں دکھائی گئی یا چلائی گئی ہر چیز ریکارڈ کی جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"اسکرین ریکارڈ کریں"</string>
@@ -136,11 +138,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 +523,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 +583,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 +860,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"تلاش کا کوئی نتیجہ نہیں ہے"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..7f6275c 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekran yozib olinsinmi?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bitta ilovani yozib olish"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Butun ekranni yozib olish"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Butun ekranni yozib olishda ekranda koʻrsatilgan barcha axborotlar yozib olinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Ilovani yozib olishda ilova koʻrsatilgan yoki ijro etilgan barcha axborotlar yozib olinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranni yozib olish"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Joriy ilova"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qulayliklar"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hech narsa topilmadi"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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..388ebb8 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ghi màn hình?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ghi một ứng dụng"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ghi toàn màn hình"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Khi bạn ghi toàn màn hình, mọi nội dung trên màn hình của bạn đều được ghi. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Khi bạn ghi một ứng dụng, mọi nội dung xuất hiện hoặc phát trong ứng dụng đó sẽ đều được ghi. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ghi màn hình"</string>
@@ -136,11 +138,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 +523,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 +583,8 @@
     <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>
+    <string name="notification_settings_button_description" msgid="2441994740884163889">"Cài đặt thông báo"</string>
+    <string name="notification_history_button_description" msgid="1578657591405033383">"Nhật ký thông báo"</string>
     <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>
@@ -1399,14 +1413,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Ứng dụng hiện tại"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hỗ trợ tiếp cận"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Không có kết quả tìm kiếm nào"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1483,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-w1000dp/dimens.xml b/packages/SystemUI/res/values-w1000dp/dimens.xml
new file mode 100644
index 0000000..b3f7acd
--- /dev/null
+++ b/packages/SystemUI/res/values-w1000dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <!-- The width of the shade overlay panel (both notifications and quick settings). -->
+    <dimen name="shade_panel_width">474dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 53ba179..b463900 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要录制屏幕吗?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"录制单个应用"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"录制整个屏幕"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"录制整个屏幕时,屏幕上显示的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"录制单个应用时,该应用中显示或播放的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"录制屏幕"</string>
@@ -136,11 +138,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 +523,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 +583,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 +868,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"无搜索结果"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..9118591 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄影螢幕畫面嗎?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄影一個應用程式"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"錄影整個螢幕畫面"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"當你錄影整個螢幕畫面時,系統會錄影螢幕畫面上顯示的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"當你錄影應用程式時,系統會錄影該應用程式中顯示或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"錄影螢幕畫面"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"沒有相符的搜尋結果"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..2189cbf 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄製畫面嗎?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄製單一應用程式"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"錄製整個畫面"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"錄製整個畫面時,系統會錄下畫面上的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"當你錄製應用程式畫面時,系統會錄下該應用程式顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"錄製畫面"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <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>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"找不到相符的搜尋結果"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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..05b5040 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -112,6 +112,8 @@
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rekhoda isikrini sakho?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekhoda i-app eyodwa"</string>
     <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekhoda sonke isikrini"</string>
+    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (3754611651558838691) -->
+    <skip />
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Uma urekhoda sonke isikrini sakho, noma yini evela esikrinini iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Uma urekhoda i-app, noma yini evezwa noma edlala kuleyo app iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekhoda isikrini"</string>
@@ -136,11 +138,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 +523,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 +583,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>
@@ -1399,14 +1415,19 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"I-App yamanje"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ukufinyeleleka"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string>
+    <!-- no translation found for shortcut_helper_customize_mode_title (1467657117101096033) -->
+    <skip />
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string>
     <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ayikho imiphumela yosesho"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string>
+    <!-- no translation found for shortcut_helper_customize_button_text (3124983502748069338) -->
+    <skip />
+    <!-- no translation found for shortcut_helper_done_button_text (7249905942125386191) -->
+    <skip />
     <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 +1485,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/config.xml b/packages/SystemUI/res/values/config.xml
index f96a0b9..d4a52c3 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -338,6 +338,9 @@
     <!-- Whether to show the full screen user switcher. -->
     <bool name="config_enableFullscreenUserSwitcher">false</bool>
 
+    <!-- Whether to go to the launcher when unlocking via an occluding app -->
+    <bool name="config_goToHomeFromOccludedApps">false</bool>
+
     <!-- Determines whether the shell features all run on another thread. -->
     <bool name="config_enableShellMainThread">true</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c2d942f..7fa2879 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -577,6 +577,19 @@
 
     <dimen name="notification_panel_margin_horizontal">0dp</dimen>
 
+    <!--
+    The width of the shade overlay panel (both notifications and quick settings). On Compact screens
+    in portrait orientation (< w600dp) this is ignored, and the shade layout takes up the full
+    screen width.
+    -->
+    <dimen name="shade_panel_width">412dp</dimen>
+
+    <!--
+    The horizontal distance between the shade overlay panel (both notifications and quick settings)
+    and the edge of the screen. This is zero only on Compact screens (< sw600dp).
+    -->
+    <dimen name="shade_panel_margin_horizontal">0dp</dimen>
+
     <dimen name="brightness_mirror_height">48dp</dimen>
 
     <dimen name="volume_dialog_panel_transparent_padding_right">8dp</dimen>
@@ -2051,7 +2064,7 @@
     <!-- Volume start -->
     <dimen name="volume_dialog_background_corner_radius">30dp</dimen>
     <dimen name="volume_dialog_width">60dp</dimen>
-    <dimen name="volume_dialog_vertical_padding">6dp</dimen>
+    <dimen name="volume_dialog_vertical_padding">10dp</dimen>
     <dimen name="volume_dialog_components_spacing">8dp</dimen>
     <dimen name="volume_dialog_floating_sliders_spacing">8dp</dimen>
     <dimen name="volume_dialog_floating_sliders_vertical_padding">10dp</dimen>
@@ -2065,10 +2078,10 @@
     <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>
+    <dimen name="volume_dialog_ringer_horizontal_padding">10dp</dimen>
+    <dimen name="volume_dialog_ringer_drawer_button_size">40dp</dimen>
+    <dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen>
+    <dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
+    <dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</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 fe720b9..1766cdf 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]-->
@@ -1796,6 +1797,7 @@
     <string name="volume_panel_spatial_audio_tracking">Head Tracking</string>
 
     <string name="volume_ringer_change">Tap to change ringer mode</string>
+    <string name="volume_ringer_mode">ringer mode</string>
 
     <!-- Hint for accessibility. For example: double tap to mute [CHAR_LIMIT=NONE] -->
     <string name="volume_ringer_hint_mute">mute</string>
@@ -3732,6 +3734,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] -->
@@ -3743,6 +3749,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.
@@ -3917,10 +3931,10 @@
     </string>
     <!-- Title for the Reset Tiles dialog in QS Edit mode. [CHAR LIMIT=NONE] -->
     <string name="qs_edit_mode_reset_dialog_title">
-        Reset tiles
+        Reset all tiles?
     </string>
     <!-- Content of the Reset Tiles dialog in QS Edit mode. [CHAR LIMIT=NONE] -->
     <string name="qs_edit_mode_reset_dialog_content">
-        Reset tiles to their original order and sizes?
+        All Quick Settings tiles will reset to the device’s original settings
     </string>
 </resources>
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 8b2cf7a..8b59370 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -585,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() {
@@ -2523,6 +2529,11 @@
         if (mUserTracker.isUserSwitching()) {
             handleUserSwitching(mUserTracker.getUserId(), () -> {});
         }
+
+        // Force the cache to be initialized
+        mBackgroundExecutor.execute(() -> {
+            getSubscriptionInfo(/* forceReload= */ true);
+        });
     }
 
     @VisibleForTesting
@@ -3851,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/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/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 69ab976..12b5fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -33,7 +33,6 @@
 import android.hardware.biometrics.BiometricAuthenticator.Modality;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager.Authenticators;
-import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -323,7 +322,7 @@
         final boolean isLandscape = mContext.getResources().getConfiguration().orientation
                 == Configuration.ORIENTATION_LANDSCAPE;
         mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
-        mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mEffectiveUserId,
+        mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mConfig.mUserId,
                 getRequestId(), biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName,
                 false /*onSwitchToCredential*/, isLandscape);
 
@@ -676,10 +675,8 @@
 
         final Runnable endActionRunnable = () -> {
             setVisibility(View.INVISIBLE);
-            if (Flags.customBiometricPrompt()) {
                 // TODO(b/288175645): resetPrompt calls should be lifecycle aware
                 mPromptSelectorInteractorProvider.get().resetPrompt(getRequestId());
-            }
             removeWindowIfAttached();
         };
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
index 4997370..b070068 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
@@ -3,6 +3,7 @@
 import android.app.admin.DevicePolicyManager
 import android.app.admin.DevicePolicyResources
 import android.content.Context
+import android.hardware.biometrics.Flags
 import android.os.UserManager
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockscreenCredential
@@ -71,13 +72,22 @@
         // Request LockSettingsService to return the Gatekeeper Password in the
         // VerifyCredentialResponse so that we can request a Gatekeeper HAT with the
         // Gatekeeper Password and operationId.
-        val effectiveUserId = request.userInfo.deviceCredentialOwnerId
+        var effectiveUserId = request.userInfo.userIdForPasswordEntry
         val response =
-            lockPatternUtils.verifyCredential(
-                credential,
-                effectiveUserId,
-                LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE
-            )
+            if (Flags.privateSpaceBp() && effectiveUserId != request.userInfo.userId) {
+                effectiveUserId = request.userInfo.userId
+                lockPatternUtils.verifyTiedProfileChallenge(
+                    credential,
+                    request.userInfo.userId,
+                    LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
+                )
+            } else {
+                lockPatternUtils.verifyCredential(
+                    credential,
+                    effectiveUserId,
+                    LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
+                )
+            }
 
         if (response.isMatched) {
             lockPatternUtils.userPresent(effectiveUserId)
@@ -91,7 +101,7 @@
                 lockPatternUtils.verifyGatekeeperPasswordHandle(
                     pwHandle,
                     request.operationInfo.gatekeeperChallenge,
-                    effectiveUserId
+                    effectiveUserId,
                 )
             val hat = gkResponse.gatekeeperHAT
             lockPatternUtils.removeGatekeeperPasswordHandle(pwHandle)
@@ -108,7 +118,7 @@
                     CredentialStatus.Fail.Throttled(
                         applicationContext.getString(
                             R.string.biometric_dialog_credential_too_many_attempts,
-                            remaining / 1000
+                            remaining / 1000,
                         )
                     )
                 )
@@ -129,10 +139,10 @@
                         applicationContext.getString(
                             R.string.biometric_dialog_credential_attempts_before_wipe,
                             numAttempts,
-                            maxAttempts
+                            maxAttempts,
                         ),
                         remainingAttempts,
-                        fetchFinalAttemptMessageOrNull(request, remainingAttempts)
+                        fetchFinalAttemptMessageOrNull(request, remainingAttempts),
                     )
                 )
             }
@@ -150,9 +160,9 @@
                 devicePolicyManager,
                 userManager.getUserTypeForWipe(
                     devicePolicyManager,
-                    request.userInfo.deviceCredentialOwnerId
+                    request.userInfo.deviceCredentialOwnerId,
                 ),
-                remainingAttempts
+                remainingAttempts,
             )
         } else {
             null
@@ -205,7 +215,7 @@
     }
 
 private fun Context.getLastAttemptBeforeWipeDeviceMessage(
-    request: BiometricPromptRequest.Credential,
+    request: BiometricPromptRequest.Credential
 ): String {
     val id =
         when (request) {
@@ -249,7 +259,7 @@
 }
 
 private fun Context.getLastAttemptBeforeWipeUserMessage(
-    request: BiometricPromptRequest.Credential,
+    request: BiometricPromptRequest.Credential
 ): String {
     val resId =
         when (request) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index 6da5e42..008fb26 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.biometrics.domain.interactor
 
-import android.hardware.biometrics.Flags
 import android.hardware.biometrics.PromptInfo
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.biometrics.Utils
@@ -104,6 +103,7 @@
 constructor(
     fingerprintPropertyRepository: FingerprintPropertyRepository,
     private val displayStateInteractor: DisplayStateInteractor,
+    private val credentialInteractor: CredentialInteractor,
     private val promptRepository: PromptRepository,
     private val lockPatternUtils: LockPatternUtils,
 ) : PromptSelectorInteractor {
@@ -177,7 +177,7 @@
 
     override fun setPrompt(
         promptInfo: PromptInfo,
-        effectiveUserId: Int,
+        userId: Int,
         requestId: Long,
         modalities: BiometricModalities,
         challenge: Long,
@@ -185,10 +185,10 @@
         onSwitchToCredential: Boolean,
         isLandscape: Boolean,
     ) {
+        val effectiveUserId = credentialInteractor.getCredentialOwnerOrSelfId(userId)
         val hasCredentialViewShown = promptKind.value.isCredential()
         val showBpForCredential =
-            Flags.customBiometricPrompt() &&
-                !Utils.isBiometricAllowed(promptInfo) &&
+            !Utils.isBiometricAllowed(promptInfo) &&
                 isDeviceCredentialAllowed(promptInfo) &&
                 promptInfo.contentView != null &&
                 !promptInfo.isContentViewMoreOptionsButtonUsed
@@ -211,10 +211,7 @@
                                 PromptKind.Biometric.PaneType.ONE_PANE_NO_SENSOR_LANDSCAPE
                             else -> PromptKind.Biometric.PaneType.TWO_PANE_LANDSCAPE
                         }
-                    PromptKind.Biometric(
-                        modalities,
-                        paneType = paneType,
-                    )
+                    PromptKind.Biometric(modalities, paneType = paneType)
                 } else {
                     PromptKind.Biometric(modalities)
                 }
@@ -224,7 +221,7 @@
 
         promptRepository.setPrompt(
             promptInfo = promptInfo,
-            userId = effectiveUserId,
+            userId = userId,
             requestId = requestId,
             gatekeeperChallenge = challenge,
             kind = kind,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 2df5f16..db4b0f2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -22,7 +22,6 @@
 import android.hardware.biometrics.BiometricAuthenticator
 import android.hardware.biometrics.BiometricConstants
 import android.hardware.biometrics.BiometricPrompt
-import android.hardware.biometrics.Flags
 import android.hardware.face.FaceManager
 import android.util.Log
 import android.view.MotionEvent
@@ -93,7 +92,7 @@
         val attributes =
             view.context.obtainStyledAttributes(
                 R.style.TextAppearance_AuthCredential_Indicator,
-                intArrayOf(android.R.attr.textColor)
+                intArrayOf(android.R.attr.textColor),
             )
         val textColorHint = attributes.getColor(0, 0)
         attributes.recycle()
@@ -130,13 +129,13 @@
             object : AccessibilityDelegateCompat() {
                 override fun onInitializeAccessibilityNodeInfo(
                     host: View,
-                    info: AccessibilityNodeInfoCompat
+                    info: AccessibilityNodeInfoCompat,
                 ) {
                     super.onInitializeAccessibilityNodeInfo(host, info)
                     info.addAction(
                         AccessibilityActionCompat(
                             AccessibilityNodeInfoCompat.ACTION_CLICK,
-                            view.context.getString(R.string.biometric_dialog_cancel_authentication)
+                            view.context.getString(R.string.biometric_dialog_cancel_authentication),
                         )
                     )
                 }
@@ -189,13 +188,11 @@
             subtitleView.text = viewModel.subtitle.first()
             descriptionView.text = viewModel.description.first()
 
-            if (Flags.customBiometricPrompt()) {
-                BiometricCustomizedViewBinder.bind(
-                    customizedViewContainer,
-                    viewModel.contentView.first(),
-                    legacyCallback
-                )
-            }
+            BiometricCustomizedViewBinder.bind(
+                customizedViewContainer,
+                viewModel.contentView.first(),
+                legacyCallback,
+            )
 
             // set button listeners
             negativeButton.setOnClickListener { legacyCallback.onButtonNegative() }
@@ -233,10 +230,7 @@
             lifecycleScope.launch {
                 viewModel.hideSensorIcon.collect { showWithoutIcon ->
                     if (!showWithoutIcon) {
-                        PromptIconViewBinder.bind(
-                            iconView,
-                            viewModel,
-                        )
+                        PromptIconViewBinder.bind(iconView, viewModel)
                     }
                 }
             }
@@ -421,7 +415,7 @@
                     launch {
                         viewModel.onAnnounceAccessibilityHint(
                             event,
-                            accessibilityManager.isTouchExplorationEnabled
+                            accessibilityManager.isTouchExplorationEnabled,
                         )
                     }
                     false
@@ -444,10 +438,7 @@
                                         haptics.flag,
                                     )
                                 } else {
-                                    vibratorHelper.performHapticFeedback(
-                                        view,
-                                        haptics.constant,
-                                    )
+                                    vibratorHelper.performHapticFeedback(view, haptics.constant)
                                 }
                             }
                             is PromptViewModel.HapticsToPlay.MSDL -> {
@@ -561,14 +552,12 @@
             viewModel.showAuthenticated(
                 modality = authenticatedModality,
                 dismissAfterDelay = 500,
-                helpMessage = if (msgId != null) applicationContext.getString(msgId) else ""
+                helpMessage = if (msgId != null) applicationContext.getString(msgId) else "",
             )
         }
     }
 
-    private fun getHelpForSuccessfulAuthentication(
-        authenticatedModality: BiometricModality,
-    ): Int? {
+    private fun getHelpForSuccessfulAuthentication(authenticatedModality: BiometricModality): Int? {
         // for coex, show a message when face succeeds after fingerprint has also started
         if (authenticatedModality != BiometricModality.Face) {
             return null
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 3ea91f0..39543e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -1,6 +1,5 @@
 package com.android.systemui.biometrics.ui.binder
 
-import android.hardware.biometrics.Flags
 import android.view.View
 import android.view.ViewGroup
 import android.widget.Button
@@ -67,7 +66,7 @@
                     updateForContentDimensions(
                         containerWidth,
                         containerHeight,
-                        0 // animateDurationMs
+                        0, // animateDurationMs
                     )
                 }
             }
@@ -81,13 +80,11 @@
 
                         subtitleView.textOrHide = header.subtitle
                         descriptionView.textOrHide = header.description
-                        if (Flags.customBiometricPrompt()) {
-                            BiometricCustomizedViewBinder.bind(
-                                customizedViewContainer,
-                                header.contentView,
-                                legacyCallback
-                            )
-                        }
+                        BiometricCustomizedViewBinder.bind(
+                            customizedViewContainer,
+                            header.contentView,
+                            legacyCallback,
+                        )
 
                         iconView?.setImageDrawable(header.icon)
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 761c3da..0c5c723 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -2,7 +2,6 @@
 
 import android.content.Context
 import android.graphics.drawable.Drawable
-import android.hardware.biometrics.Flags.customBiometricPrompt
 import android.hardware.biometrics.PromptContentView
 import android.text.InputType
 import com.android.internal.widget.LockPatternView
@@ -36,21 +35,17 @@
     val header: Flow<CredentialHeaderViewModel> =
         combine(
             credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>(),
-            credentialInteractor.showTitleOnly
+            credentialInteractor.showTitleOnly,
         ) { request, showTitleOnly ->
-            val flagEnabled = customBiometricPrompt()
-            val showTitleOnlyForCredential = showTitleOnly && flagEnabled
             BiometricPromptHeaderViewModelImpl(
                 request,
                 user = request.userInfo,
                 title = request.title,
-                subtitle = if (showTitleOnlyForCredential) "" else request.subtitle,
-                contentView =
-                    if (flagEnabled && !showTitleOnlyForCredential) request.contentView else null,
-                description =
-                    if (flagEnabled && request.contentView != null) "" else request.description,
+                subtitle = if (showTitleOnly) "" else request.subtitle,
+                contentView = if (!showTitleOnly) request.contentView else null,
+                description = if (request.contentView != null) "" else request.description,
                 icon = applicationContext.asLockIcon(request.userInfo.deviceCredentialOwnerId),
-                showEmergencyCallButton = request.showEmergencyCallButton
+                showEmergencyCallButton = request.showEmergencyCallButton,
             )
         }
 
@@ -125,7 +120,7 @@
     /** Check a pattern and update [validatedAttestation] or [remainingAttempts]. */
     suspend fun checkCredential(
         pattern: List<LockPatternView.Cell>,
-        header: CredentialHeaderViewModel
+        header: CredentialHeaderViewModel,
     ) = checkCredential(credentialInteractor.checkCredential(header.asRequest(), pattern = pattern))
 
     private suspend fun checkCredential(result: CredentialStatus) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 0ac9405..cbf783d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -27,7 +27,6 @@
 import android.graphics.drawable.Drawable
 import android.hardware.biometrics.BiometricFingerprintConstants
 import android.hardware.biometrics.BiometricPrompt
-import android.hardware.biometrics.Flags.customBiometricPrompt
 import android.hardware.biometrics.PromptContentView
 import android.os.UserHandle
 import android.text.TextPaint
@@ -145,13 +144,13 @@
                     rotatedBounds,
                     params.naturalDisplayWidth,
                     params.naturalDisplayHeight,
-                    rotation.ordinal
+                    rotation.ordinal,
                 )
                 Rect(
                     rotatedBounds.left,
                     rotatedBounds.top,
                     params.logicalDisplayWidth - rotatedBounds.right,
-                    params.logicalDisplayHeight - rotatedBounds.bottom
+                    params.logicalDisplayHeight - rotatedBounds.bottom,
                 )
             }
             .distinctUntilChanged()
@@ -263,7 +262,7 @@
                 promptKind,
                 displayStateInteractor.isLargeScreen,
                 displayStateInteractor.currentRotation,
-                modalities
+                modalities,
             ) { forceLarge, promptKind, isLargeScreen, rotation, modalities ->
                 when {
                     forceLarge ||
@@ -351,7 +350,7 @@
                                 0,
                                 0,
                                 landscapeMediumHorizontalPadding,
-                                landscapeMediumBottomPadding
+                                landscapeMediumBottomPadding,
                             )
                         }
                     PromptPosition.Left ->
@@ -365,7 +364,7 @@
                                 landscapeMediumHorizontalPadding,
                                 0,
                                 0,
-                                landscapeMediumBottomPadding
+                                landscapeMediumBottomPadding,
                             )
                         }
                     PromptPosition.Top ->
@@ -474,7 +473,7 @@
         promptSelectorInteractor.prompt
             .map {
                 when {
-                    !(customBiometricPrompt()) || it == null -> Pair(null, "")
+                    it == null -> Pair(null, "")
                     else -> context.getUserBadgedLogoInfo(it, iconProvider, activityTaskManager)
                 }
             }
@@ -490,9 +489,7 @@
 
     /** Custom content view for the prompt. */
     val contentView: Flow<PromptContentView?> =
-        promptSelectorInteractor.prompt
-            .map { if (customBiometricPrompt()) it?.contentView else null }
-            .distinctUntilChanged()
+        promptSelectorInteractor.prompt.map { it?.contentView }.distinctUntilChanged()
 
     private val originalDescription =
         promptSelectorInteractor.prompt.map { it?.description ?: "" }.distinctUntilChanged()
@@ -521,7 +518,7 @@
                 val attributes =
                     context.obtainStyledAttributes(
                         R.style.TextAppearance_AuthCredential_Title,
-                        intArrayOf(android.R.attr.textSize)
+                        intArrayOf(android.R.attr.textSize),
                     )
                 val paint = TextPaint()
                 paint.textSize = attributes.getDimensionPixelSize(0, 0).toFloat()
@@ -566,7 +563,7 @@
     private fun getHorizontalPadding(
         size: PromptSize,
         modalities: BiometricModalities,
-        hasOnlyOneLineTitle: Boolean
+        hasOnlyOneLineTitle: Boolean,
     ) =
         if (size.isSmall) {
             -smallHorizontalGuidelinePadding
@@ -582,21 +579,13 @@
 
     /** If the indicator (help, error) message should be shown. */
     val isIndicatorMessageVisible: Flow<Boolean> =
-        combine(
-            size,
-            position,
-            message,
-        ) { size, _, message ->
+        combine(size, position, message) { size, _, message ->
             size.isMedium && message.message.isNotBlank()
         }
 
     /** If the auth is pending confirmation and the confirm button should be shown. */
     val isConfirmButtonVisible: Flow<Boolean> =
-        combine(
-            size,
-            position,
-            isPendingConfirmation,
-        ) { size, _, isPendingConfirmation ->
+        combine(size, position, isPendingConfirmation) { size, _, isPendingConfirmation ->
             size.isNotSmall && isPendingConfirmation
         }
 
@@ -606,24 +595,22 @@
 
     /** If the negative button should be shown. */
     val isNegativeButtonVisible: Flow<Boolean> =
-        combine(
+        combine(size, position, isAuthenticated, promptSelectorInteractor.isCredentialAllowed) {
             size,
-            position,
-            isAuthenticated,
-            promptSelectorInteractor.isCredentialAllowed,
-        ) { size, _, authState, credentialAllowed ->
+            _,
+            authState,
+            credentialAllowed ->
             size.isNotSmall && authState.isNotAuthenticated && !credentialAllowed
         }
 
     /** If the cancel button should be shown (. */
     val isCancelButtonVisible: Flow<Boolean> =
-        combine(
+        combine(size, position, isAuthenticated, isNegativeButtonVisible, isConfirmButtonVisible) {
             size,
-            position,
-            isAuthenticated,
-            isNegativeButtonVisible,
-            isConfirmButtonVisible,
-        ) { size, _, authState, showNegativeButton, showConfirmButton ->
+            _,
+            authState,
+            showNegativeButton,
+            showConfirmButton ->
             size.isNotSmall && authState.isAuthenticated && !showNegativeButton && showConfirmButton
         }
 
@@ -633,33 +620,28 @@
      * fingerprint sensor.
      */
     val canTryAgainNow: Flow<Boolean> =
-        combine(
-            _canTryAgainNow,
+        combine(_canTryAgainNow, size, position, isAuthenticated, isRetrySupported) {
+            readyToTryAgain,
             size,
-            position,
-            isAuthenticated,
-            isRetrySupported,
-        ) { readyToTryAgain, size, _, authState, supportsRetry ->
+            _,
+            authState,
+            supportsRetry ->
             readyToTryAgain && size.isNotSmall && supportsRetry && authState.isNotAuthenticated
         }
 
     /** If the try again button show be shown (only the button, see [canTryAgainNow]). */
     val isTryAgainButtonVisible: Flow<Boolean> =
-        combine(
-            canTryAgainNow,
-            modalities,
-        ) { tryAgainIsPossible, modalities ->
+        combine(canTryAgainNow, modalities) { tryAgainIsPossible, modalities ->
             tryAgainIsPossible && modalities.hasFaceOnly
         }
 
     /** If the credential fallback button show be shown. */
     val isCredentialButtonVisible: Flow<Boolean> =
-        combine(
+        combine(size, position, isAuthenticated, promptSelectorInteractor.isCredentialAllowed) {
             size,
-            position,
-            isAuthenticated,
-            promptSelectorInteractor.isCredentialAllowed,
-        ) { size, _, authState, credentialAllowed ->
+            _,
+            authState,
+            credentialAllowed ->
             size.isMedium && authState.isNotAuthenticated && credentialAllowed
         }
 
@@ -759,10 +741,7 @@
      *
      * Ignored if the user has already authenticated.
      */
-    suspend fun showTemporaryHelp(
-        message: String,
-        messageAfterHelp: String = "",
-    ) = coroutineScope {
+    suspend fun showTemporaryHelp(message: String, messageAfterHelp: String = "") = coroutineScope {
         if (_isAuthenticated.value.isAuthenticated) {
             return@coroutineScope
         }
@@ -910,13 +889,13 @@
                 udfpsUtils.getTouchInNativeCoordinates(
                     event.getPointerId(0),
                     event,
-                    udfpsOverlayParams.value
+                    udfpsOverlayParams.value,
                 )
             if (
                 !udfpsUtils.isWithinSensorArea(
                     event.getPointerId(0),
                     event,
-                    udfpsOverlayParams.value
+                    udfpsOverlayParams.value,
                 )
             ) {
                 _accessibilityHint.emit(
@@ -925,7 +904,7 @@
                         context,
                         scaledTouch.x,
                         scaledTouch.y,
-                        udfpsOverlayParams.value
+                        udfpsOverlayParams.value,
                     )
                 )
             }
@@ -948,10 +927,7 @@
             if (msdlFeedback()) {
                 HapticsToPlay.MSDL(MSDLToken.UNLOCK, authInteractionProperties)
             } else {
-                HapticsToPlay.HapticConstant(
-                    HapticFeedbackConstants.BIOMETRIC_CONFIRM,
-                    flag = null,
-                )
+                HapticsToPlay.HapticConstant(HapticFeedbackConstants.BIOMETRIC_CONFIRM, flag = null)
             }
         _hapticsToPlay.value = haptics
     }
@@ -961,10 +937,7 @@
             if (msdlFeedback()) {
                 HapticsToPlay.MSDL(MSDLToken.FAILURE, authInteractionProperties)
             } else {
-                HapticsToPlay.HapticConstant(
-                    HapticFeedbackConstants.BIOMETRIC_REJECT,
-                    flag = null,
-                )
+                HapticsToPlay.HapticConstant(HapticFeedbackConstants.BIOMETRIC_REJECT, flag = null)
             }
         _hapticsToPlay.value = haptics
     }
@@ -1006,7 +979,7 @@
 private fun Context.getUserBadgedLogoInfo(
     prompt: BiometricPromptRequest.Biometric,
     iconProvider: IconProvider,
-    activityTaskManager: ActivityTaskManager
+    activityTaskManager: ActivityTaskManager,
 ): Pair<Drawable?, String> {
     var icon: Drawable? =
         if (prompt.logoBitmap != null) BitmapDrawable(resources, prompt.logoBitmap) else null
@@ -1045,7 +1018,7 @@
                 packageManager
                     .getUserBadgedLabel(
                         packageManager.getApplicationLabel(appInfo),
-                        UserHandle.of(userId)
+                        UserHandle.of(userId),
                     )
                     .toString()
         }
@@ -1070,7 +1043,7 @@
 
 private fun BiometricPromptRequest.Biometric.getApplicationInfo(
     context: Context,
-    componentNameForLogo: ComponentName?
+    componentNameForLogo: ComponentName?,
 ): ApplicationInfo? {
     val packageName =
         when {
@@ -1088,7 +1061,7 @@
         try {
             context.packageManager.getApplicationInfo(
                 packageName,
-                PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER
+                PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_ANY_USER,
             )
         } catch (e: PackageManager.NameNotFoundException) {
             Log.w(PromptViewModel.TAG, "Cannot find application info for $opPackageName", e)
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/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index a33e0ac..44dd34a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -32,6 +32,8 @@
 import com.android.systemui.communal.shared.log.CommunalMetricsLogger
 import com.android.systemui.communal.shared.log.CommunalStatsLogProxyImpl
 import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelperImpl
 import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.communal.util.CommunalColorsImpl
 import com.android.systemui.communal.widgets.CommunalWidgetModule
@@ -65,6 +67,7 @@
             CommunalSettingsRepositoryModule::class,
             CommunalSmartspaceRepositoryModule::class,
             CommunalStartableModule::class,
+            GlanceableHubWidgetManagerModule::class,
         ]
 )
 interface CommunalModule {
@@ -91,6 +94,11 @@
         impl: CommunalSceneTransitionInteractor
     ): CoreStartable
 
+    @Binds
+    fun bindGlanceableHubMultiUserHelper(
+        impl: GlanceableHubMultiUserHelperImpl
+    ): GlanceableHubMultiUserHelper
+
     companion object {
         const val LOGGABLE_PREFIXES = "loggable_prefixes"
         const val LAUNCHER_PACKAGE = "launcher_package"
@@ -106,19 +114,14 @@
                     sceneKeys = listOf(CommunalScenes.Blank, CommunalScenes.Communal),
                     initialSceneKey = CommunalScenes.Blank,
                     navigationDistances =
-                        mapOf(
-                            CommunalScenes.Blank to 0,
-                            CommunalScenes.Communal to 1,
-                        ),
+                        mapOf(CommunalScenes.Blank to 0, CommunalScenes.Communal to 1),
                 )
             return SceneDataSourceDelegator(applicationScope, config)
         }
 
         @Provides
         @SysUISingleton
-        fun providesCommunalBackupUtils(
-            @Application context: Context,
-        ): CommunalBackupUtils {
+        fun providesCommunalBackupUtils(@Application context: Context): CommunalBackupUtils {
             return CommunalBackupUtils(context)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
index 6cbf540..2d19b02 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalStartableModule.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.communal.CommunalSceneStartable
 import com.android.systemui.communal.log.CommunalLoggerStartable
 import com.android.systemui.communal.widgets.CommunalAppWidgetHostStartable
+import com.android.systemui.dagger.qualifiers.PerUser
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -48,6 +49,7 @@
 
     @Binds
     @IntoMap
+    @PerUser
     @ClassKey(CommunalAppWidgetHostStartable::class)
     fun bindCommunalAppWidgetHostStartable(impl: CommunalAppWidgetHostStartable): CoreStartable
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/GlanceableHubWidgetManagerModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/GlanceableHubWidgetManagerModule.kt
new file mode 100644
index 0000000..8c07583
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/GlanceableHubWidgetManagerModule.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.dagger
+
+import com.android.server.servicewatcher.ServiceWatcher.ServiceSupplier
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManagerServiceInfo
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManagerServiceSupplier
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManagerServiceWatcherFactoryImpl
+import com.android.systemui.communal.widgets.ServiceWatcherFactory
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface GlanceableHubWidgetManagerModule {
+    @Binds
+    fun bindServiceSupplier(
+        impl: GlanceableHubWidgetManagerServiceSupplier
+    ): ServiceSupplier<GlanceableHubWidgetManagerServiceInfo?>
+
+    @Binds
+    fun bindServiceWatcherFactory(
+        impl: GlanceableHubWidgetManagerServiceWatcherFactoryImpl
+    ): ServiceWatcherFactory<GlanceableHubWidgetManagerServiceInfo?>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
index 17f4f0c..e72088f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.communal.data.db
 
 import android.content.Context
+import android.os.Process
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import androidx.room.Database
@@ -24,6 +25,7 @@
 import androidx.room.RoomDatabase
 import androidx.room.migration.Migration
 import androidx.sqlite.db.SupportSQLiteDatabase
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelperImpl
 import com.android.systemui.res.R
 
 @Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 4)
@@ -44,6 +46,11 @@
          *   new instance is created.
          */
         fun getInstance(context: Context, callback: Callback? = null): CommunalDatabase {
+            with(GlanceableHubMultiUserHelperImpl(Process.myUserHandle())) {
+                // Assert that the database is never accessed from a headless system user.
+                assertNotInHeadlessSystemUser()
+            }
+
             if (instance == null) {
                 instance =
                     Room.databaseBuilder(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
index 258480e..3d40aa7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -25,6 +25,7 @@
 import androidx.room.Transaction
 import androidx.room.Update
 import androidx.sqlite.db.SupportSQLiteDatabase
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.communal.nano.CommunalHubState
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.widgets.CommunalWidgetHost
@@ -39,7 +40,6 @@
 import javax.inject.Provider
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * Callback that will be invoked when the Room database is created. Then the database will be
@@ -224,9 +224,9 @@
     ): Long {
         val widgets = getWidgetsNow()
 
-        // If rank is not specified, rank it last by finding the current maximum rank and increment
-        // by 1. If the new widget is the first widget, set the rank to 0.
-        val newRank = rank ?: widgets.keys.maxOfOrNull { it.rank + 1 } ?: 0
+        // If rank is not specified (null or less than 0), rank it last by finding the current
+        // maximum rank and increment by 1. If the new widget is the first widget, set rank to 0.
+        val newRank = rank?.takeIf { it >= 0 } ?: widgets.keys.maxOfOrNull { it.rank + 1 } ?: 0
 
         // Shift widgets after [rank], unless widget is added at the end.
         if (rank != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index e164587..29569f8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -21,6 +21,7 @@
 import android.content.ComponentName
 import android.os.UserHandle
 import android.os.UserManager
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Flags.communalWidgetResizing
 import com.android.systemui.common.data.repository.PackageChangeRepository
 import com.android.systemui.common.shared.model.PackageInstallSession
@@ -51,11 +52,10 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Encapsulates the state of widgets for communal mode. */
 interface CommunalWidgetRepository {
-    /** A flow of information about active communal widgets stored in database. */
+    /** A flow of the list of Glanceable Hub widgets ordered by rank. */
     val communalWidgets: Flow<List<CommunalWidgetContentModel>>
 
     /**
@@ -106,8 +106,12 @@
     fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>)
 }
 
+/**
+ * The local implementation of the [CommunalWidgetRepository] that should be injected in a
+ * foreground user process.
+ */
 @SysUISingleton
-class CommunalWidgetRepositoryImpl
+class CommunalWidgetRepositoryLocalImpl
 @Inject
 constructor(
     private val appWidgetHost: CommunalAppWidgetHost,
@@ -123,7 +127,7 @@
     private val defaultWidgetPopulation: DefaultWidgetPopulation,
 ) : CommunalWidgetRepository {
     companion object {
-        const val TAG = "CommunalWidgetRepository"
+        const val TAG = "CommunalWidgetRepositoryLocalImpl"
     }
 
     private val logger = Logger(logBuffer, TAG)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
index b502fb1..824edcb 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -17,11 +17,24 @@
 
 package com.android.systemui.communal.data.repository
 
-import dagger.Binds
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import dagger.Lazy
 import dagger.Module
+import dagger.Provides
 
 @Module
 interface CommunalWidgetRepositoryModule {
-    @Binds
-    fun communalWidgetRepository(impl: CommunalWidgetRepositoryImpl): CommunalWidgetRepository
+    companion object {
+        @Provides
+        fun provideCommunalWidgetRepository(
+            localImpl: Lazy<CommunalWidgetRepositoryLocalImpl>,
+            remoteImpl: Lazy<CommunalWidgetRepositoryRemoteImpl>,
+            helper: GlanceableHubMultiUserHelper,
+        ): CommunalWidgetRepository {
+            // Provide an implementation based on the current user.
+            return if (helper.glanceableHubHsumFlagEnabled && helper.isInHeadlessSystemUser())
+                remoteImpl.get()
+            else localImpl.get()
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryRemoteImpl.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryRemoteImpl.kt
new file mode 100644
index 0000000..6682186
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryRemoteImpl.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import android.content.ComponentName
+import android.os.UserHandle
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManager
+import com.android.systemui.communal.widgets.WidgetConfigurator
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+/**
+ * The remote implementation of the [CommunalWidgetRepository] that should be injected in a headless
+ * system user process. This implementation receives widget data from and routes requests to the
+ * remote service in the foreground user.
+ */
+@SysUISingleton
+class CommunalWidgetRepositoryRemoteImpl
+@Inject
+constructor(
+    @Background private val bgScope: CoroutineScope,
+    private val glanceableHubWidgetManager: GlanceableHubWidgetManager,
+    glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
+) : CommunalWidgetRepository {
+
+    init {
+        // This is the implementation for the headless system user. For the foreground user
+        // implementation see [CommunalWidgetRepositoryLocalImpl].
+        glanceableHubMultiUserHelper.assertInHeadlessSystemUser()
+    }
+
+    override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
+        glanceableHubWidgetManager.widgets
+
+    override fun addWidget(
+        provider: ComponentName,
+        user: UserHandle,
+        rank: Int?,
+        configurator: WidgetConfigurator?,
+    ) {
+        bgScope.launch { glanceableHubWidgetManager.addWidget(provider, user, rank, configurator) }
+    }
+
+    override fun deleteWidget(widgetId: Int) {
+        bgScope.launch { glanceableHubWidgetManager.deleteWidget(widgetId) }
+    }
+
+    override fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) {
+        bgScope.launch { glanceableHubWidgetManager.updateWidgetOrder(widgetIdToRankMap) }
+    }
+
+    override fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {
+        bgScope.launch {
+            glanceableHubWidgetManager.resizeWidget(appWidgetId, spanY, widgetIdToRankMap)
+        }
+    }
+
+    override fun restoreWidgets(oldToNewWidgetIdMap: Map<Int, Int>) {
+        throw IllegalStateException("Restore widgets should be performed on a foreground user")
+    }
+
+    override fun abortRestoreWidgets() {
+        throw IllegalStateException("Restore widgets should be performed on a foreground user")
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index dc24805..602fe30 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -40,7 +40,6 @@
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.shared.model.EditModeState
-import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.dagger.SysUISingleton
@@ -108,7 +107,6 @@
     keyguardInteractor: KeyguardInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     communalSettingsInteractor: CommunalSettingsInteractor,
-    private val appWidgetHost: CommunalAppWidgetHost,
     private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
     private val userTracker: UserTracker,
     private val activityStarter: ActivityStarter,
@@ -451,7 +449,6 @@
                             appWidgetId = widget.appWidgetId,
                             rank = widget.rank,
                             providerInfo = widget.providerInfo,
-                            appWidgetHost = appWidgetHost,
                             inQuietMode = isQuietModeEnabled(widget.providerInfo.profile),
                             size = CommunalContentSize.toSize(widget.spanY),
                         )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index 4c2c094..30f580e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -23,7 +23,6 @@
 import android.graphics.Bitmap
 import android.widget.RemoteViews
 import com.android.systemui.communal.shared.model.CommunalContentSize
-import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import java.util.UUID
 
 /** Encapsulates data for a communal content. */
@@ -60,7 +59,6 @@
             override val appWidgetId: Int,
             override val rank: Int,
             val providerInfo: AppWidgetProviderInfo,
-            val appWidgetHost: CommunalAppWidgetHost,
             val inQuietMode: Boolean,
             override val size: CommunalContentSize,
         ) : WidgetContent {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
index 572794d..cf80b7d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
@@ -36,7 +36,7 @@
         /** Converts from span to communal content size. */
         fun toSize(span: Int): CommunalContentSize {
             return entries.find { it.span == span }
-                ?: throw Exception("Invalid span for communal content size")
+                ?: throw IllegalArgumentException("$span is not a valid span size")
         }
     }
 }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.aidl
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.aidl
index 83cbc52..a215698e 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.aidl
@@ -13,10 +13,7 @@
  * 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.communal.shared.model;
+
+parcelable CommunalWidgetContentModel;
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
index 0c9ea78..f23347d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
@@ -19,21 +19,60 @@
 import android.appwidget.AppWidgetProviderInfo
 import android.content.ComponentName
 import android.graphics.Bitmap
+import android.os.Parcel
+import android.os.Parcelable
 import android.os.UserHandle
 
 /** Encapsulates data for a communal widget. */
-sealed interface CommunalWidgetContentModel {
+sealed interface CommunalWidgetContentModel : Parcelable {
     val appWidgetId: Int
     val rank: Int
     val spanY: Int
 
+    // Used for distinguishing subtypes when reading from a parcel.
+    val type: Int
+
     /** Widget is ready to display */
     data class Available(
         override val appWidgetId: Int,
         val providerInfo: AppWidgetProviderInfo,
         override val rank: Int,
         override val spanY: Int,
-    ) : CommunalWidgetContentModel
+    ) : CommunalWidgetContentModel {
+
+        override val type = TYPE_AVAILABLE
+
+        constructor(
+            parcel: Parcel
+        ) : this(
+            parcel.readInt(),
+            requireNotNull(parcel.readTypedObject(AppWidgetProviderInfo.CREATOR)),
+            parcel.readInt(),
+            parcel.readInt(),
+        )
+
+        override fun writeToParcel(parcel: Parcel, flags: Int) {
+            parcel.writeInt(type)
+            parcel.writeInt(appWidgetId)
+            parcel.writeTypedObject(providerInfo, flags)
+            parcel.writeInt(rank)
+            parcel.writeInt(spanY)
+        }
+
+        override fun describeContents(): Int {
+            return 0
+        }
+
+        companion object CREATOR : Parcelable.Creator<Available> {
+            override fun createFromParcel(parcel: Parcel): Available {
+                return Available(parcel)
+            }
+
+            override fun newArray(size: Int): Array<Available?> {
+                return arrayOfNulls(size)
+            }
+        }
+    }
 
     /** Widget is pending installation */
     data class Pending(
@@ -43,5 +82,61 @@
         val icon: Bitmap?,
         val user: UserHandle,
         override val spanY: Int,
-    ) : CommunalWidgetContentModel
+    ) : CommunalWidgetContentModel {
+
+        override val type = TYPE_PENDING
+
+        constructor(
+            parcel: Parcel
+        ) : this(
+            parcel.readInt(),
+            parcel.readInt(),
+            requireNotNull(parcel.readTypedObject(ComponentName.CREATOR)),
+            parcel.readTypedObject(Bitmap.CREATOR),
+            requireNotNull(parcel.readTypedObject(UserHandle.CREATOR)),
+            parcel.readInt(),
+        )
+
+        override fun writeToParcel(parcel: Parcel, flags: Int) {
+            parcel.writeInt(type)
+            parcel.writeInt(appWidgetId)
+            parcel.writeInt(rank)
+            parcel.writeTypedObject(componentName, flags)
+            parcel.writeTypedObject(icon, flags)
+            parcel.writeTypedObject(user, flags)
+            parcel.writeInt(spanY)
+        }
+
+        override fun describeContents(): Int {
+            return 0
+        }
+
+        companion object CREATOR : Parcelable.Creator<Pending> {
+            override fun createFromParcel(parcel: Parcel): Pending {
+                return Pending(parcel)
+            }
+
+            override fun newArray(size: Int): Array<Pending?> {
+                return arrayOfNulls(size)
+            }
+        }
+    }
+
+    // Used for distinguishing subtypes when reading from a parcel.
+    companion object CREATOR : Parcelable.Creator<CommunalWidgetContentModel> {
+        private const val TYPE_AVAILABLE = 0
+        private const val TYPE_PENDING = 1
+
+        override fun createFromParcel(parcel: Parcel): CommunalWidgetContentModel {
+            return when (val type = parcel.readInt()) {
+                TYPE_AVAILABLE -> Available(parcel)
+                TYPE_PENDING -> Pending(parcel)
+                else -> throw IllegalArgumentException("Unknown type: $type")
+            }
+        }
+
+        override fun newArray(size: Int): Array<CommunalWidgetContentModel?> {
+            return arrayOfNulls(size)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelper.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelper.kt
new file mode 100644
index 0000000..ef6a9ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelper.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.model
+
+import android.os.UserHandle
+import android.os.UserManager
+import com.android.systemui.Flags.secondaryUserWidgetHost
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Helper for multi-user / HSUM related functionality for the Glanceable Hub. */
+interface GlanceableHubMultiUserHelper {
+    /** Whether the Glanceable Hub in HSUM flag is enabled. */
+    val glanceableHubHsumFlagEnabled: Boolean
+
+    /** Whether the device is in headless system user mode. */
+    fun isHeadlessSystemUserMode(): Boolean
+
+    /** Whether the current process is running in the headless system user. */
+    fun isInHeadlessSystemUser(): Boolean
+
+    /**
+     * Asserts that the current process is running in the headless system user.
+     *
+     * Only throws an exception if [glanceableHubHsumFlagEnabled] is true.
+     */
+    @Throws(IllegalStateException::class) fun assertInHeadlessSystemUser()
+
+    /**
+     * Asserts that the current process is NOT running in the headless system user.
+     *
+     * Only throws an exception if [glanceableHubHsumFlagEnabled] is true.
+     */
+    @Throws(IllegalStateException::class) fun assertNotInHeadlessSystemUser()
+}
+
+@SysUISingleton
+class GlanceableHubMultiUserHelperImpl @Inject constructor(private val userHandle: UserHandle) :
+    GlanceableHubMultiUserHelper {
+
+    override val glanceableHubHsumFlagEnabled: Boolean = secondaryUserWidgetHost()
+
+    override fun isHeadlessSystemUserMode(): Boolean = UserManager.isHeadlessSystemUserMode()
+
+    override fun isInHeadlessSystemUser(): Boolean {
+        return isHeadlessSystemUserMode() && userHandle.isSystem
+    }
+
+    override fun assertInHeadlessSystemUser() {
+        if (glanceableHubHsumFlagEnabled) {
+            check(isInHeadlessSystemUser())
+        }
+    }
+
+    override fun assertNotInHeadlessSystemUser() {
+        if (glanceableHubHsumFlagEnabled) {
+            check(!isInHeadlessSystemUser())
+        }
+    }
+}
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 bde5d0f..113375f 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
@@ -76,10 +76,10 @@
             floor(spans.toDouble() / resizeMultiple).toInt() * resizeMultiple
 
         val maxSpans: Int
-            get() = roundDownToMultiple(getSpansForPx(maxHeightPx))
+            get() = roundDownToMultiple(getSpansForPx(maxHeightPx)).coerceAtLeast(currentSpan)
 
         val minSpans: Int
-            get() = roundDownToMultiple(getSpansForPx(minHeightPx))
+            get() = roundDownToMultiple(getSpansForPx(minHeightPx)).coerceAtMost(currentSpan)
     }
 
     /** Check if widget can expanded based on current drag states */
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
index d5d3a92..cc6007b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
@@ -21,11 +21,14 @@
 import android.util.SizeF
 import com.android.app.tracing.coroutines.withContextTraced as withContext
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
 import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate
 import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.communal.widgets.CommunalAppWidgetHostView
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManager
 import com.android.systemui.communal.widgets.WidgetInteractionHandler
 import com.android.systemui.dagger.qualifiers.UiBackground
+import dagger.Lazy
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
@@ -36,9 +39,11 @@
 constructor(
     @UiBackground private val uiBgContext: CoroutineContext,
     @UiBackground private val uiBgExecutor: Executor,
-    private val appWidgetHost: CommunalAppWidgetHost,
+    private val appWidgetHostLazy: Lazy<CommunalAppWidgetHost>,
     private val interactionHandler: WidgetInteractionHandler,
     private val listenerFactory: AppWidgetHostListenerDelegate.Factory,
+    private val glanceableHubWidgetManagerLazy: Lazy<GlanceableHubWidgetManager>,
+    private val multiUserHelper: GlanceableHubMultiUserHelper,
 ) {
     suspend fun createWidget(
         context: Context,
@@ -51,9 +56,25 @@
                     setExecutor(uiBgExecutor)
                     setAppWidget(model.appWidgetId, model.providerInfo)
                 }
-            // Instead of setting the view as the listener directly, we wrap the view in a delegate
-            // which ensures the callbacks always get called on the main thread.
-            appWidgetHost.setListener(model.appWidgetId, listenerFactory.create(view))
+
+            if (
+                multiUserHelper.glanceableHubHsumFlagEnabled &&
+                    multiUserHelper.isInHeadlessSystemUser()
+            ) {
+                // If the widget view is created in the headless system user, the widget host lives
+                // remotely in the foreground user, and therefore the host listener needs to be
+                // registered through the widget manager.
+                with(glanceableHubWidgetManagerLazy.get()) {
+                    setAppWidgetHostListener(model.appWidgetId, listenerFactory.create(view))
+                }
+            } else {
+                // Instead of setting the view as the listener directly, we wrap the view in a
+                // delegate which ensures the callbacks always get called on the main thread.
+                with(appWidgetHostLazy.get()) {
+                    setListener(model.appWidgetId, listenerFactory.create(view))
+                }
+            }
+
             if (size != null) {
                 view.updateAppWidgetSize(
                     /* newOptions = */ Bundle(),
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
index 4f9ed2f..70e7947 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
@@ -18,6 +18,8 @@
 
 import android.appwidget.AppWidgetHost
 import android.content.Context
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
 import javax.annotation.concurrent.GuardedBy
@@ -25,7 +27,6 @@
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.asSharedFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Communal app widget host that creates a [CommunalAppWidgetHostView]. */
 class CommunalAppWidgetHost(
@@ -33,7 +34,14 @@
     private val backgroundScope: CoroutineScope,
     hostId: Int,
     logBuffer: LogBuffer,
+    glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
 ) : AppWidgetHost(context, hostId) {
+
+    init {
+        // The app widget host should never be accessed from a headless system user.
+        glanceableHubMultiUserHelper.assertNotInHeadlessSystemUser()
+    }
+
     private val logger = Logger(logBuffer, TAG)
 
     private val _appWidgetIdToRemove = MutableSharedFlow<Int>()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
index 301da51..dec7ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -18,55 +18,118 @@
 
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.dropWhile
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.withContext
 
+// Started per user, so make sure injections are lazy to avoid instantiating unnecessary
+// dependencies.
 @SysUISingleton
 class CommunalAppWidgetHostStartable
 @Inject
 constructor(
-    private val appWidgetHost: CommunalAppWidgetHost,
-    private val communalWidgetHost: CommunalWidgetHost,
-    private val communalInteractor: CommunalInteractor,
-    private val userTracker: UserTracker,
+    private val appWidgetHostLazy: Lazy<CommunalAppWidgetHost>,
+    private val communalWidgetHostLazy: Lazy<CommunalWidgetHost>,
+    private val communalInteractorLazy: Lazy<CommunalInteractor>,
+    private val communalSettingsInteractorLazy: Lazy<CommunalSettingsInteractor>,
+    private val keyguardInteractorLazy: Lazy<KeyguardInteractor>,
+    private val userTrackerLazy: Lazy<UserTracker>,
     @Background private val bgScope: CoroutineScope,
-    @Main private val uiDispatcher: CoroutineDispatcher
+    @Main private val uiDispatcher: CoroutineDispatcher,
+    private val glanceableHubWidgetManagerLazy: Lazy<GlanceableHubWidgetManager>,
+    private val glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
 ) : CoreStartable {
 
+    private val appWidgetHost by lazy { appWidgetHostLazy.get() }
+    private val communalWidgetHost by lazy { communalWidgetHostLazy.get() }
+    private val communalInteractor by lazy { communalInteractorLazy.get() }
+    private val communalSettingsInteractor by lazy { communalSettingsInteractorLazy.get() }
+    private val keyguardInteractor by lazy { keyguardInteractorLazy.get() }
+    private val userTracker by lazy { userTrackerLazy.get() }
+    private val glanceableHubWidgetManager by lazy { glanceableHubWidgetManagerLazy.get() }
+
     override fun start() {
-        anyOf(communalInteractor.isCommunalAvailable, communalInteractor.editModeOpen)
-            // Only trigger updates on state changes, ignoring the initial false value.
-            .pairwise(false)
-            .filter { (previous, new) -> previous != new }
-            .onEach { (_, shouldListen) -> updateAppWidgetHostActive(shouldListen) }
-            .sample(communalInteractor.communalWidgets, ::Pair)
-            .onEach { (withPrev, widgets) ->
-                val (_, isActive) = withPrev
-                // The validation is performed once the hub becomes active.
-                if (isActive) {
-                    validateWidgetsAndDeleteOrphaned(widgets)
+        if (
+            glanceableHubMultiUserHelper.glanceableHubHsumFlagEnabled &&
+                glanceableHubMultiUserHelper.isInHeadlessSystemUser()
+        ) {
+            onStartInHeadlessSystemUser()
+        } else {
+            onStartInForegroundUser()
+        }
+    }
+
+    private fun onStartInForegroundUser() {
+        // Make the host active when communal becomes available, and delete widgets whose user has
+        // been removed from the system.
+        // Skipped in HSUM, because lifecycle of the host is controlled by the
+        // [GlanceableHubWidgetManagerService], and widgets are stored per user so no more orphaned
+        // widgets.
+        if (
+            !glanceableHubMultiUserHelper.glanceableHubHsumFlagEnabled ||
+                !glanceableHubMultiUserHelper.isHeadlessSystemUserMode()
+        ) {
+            anyOf(communalInteractor.isCommunalAvailable, communalInteractor.editModeOpen)
+                // Only trigger updates on state changes, ignoring the initial false value.
+                .pairwise(false)
+                .filter { (previous, new) -> previous != new }
+                .onEach { (_, shouldListen) -> updateAppWidgetHostActive(shouldListen) }
+                .sample(communalInteractor.communalWidgets, ::Pair)
+                .onEach { (withPrev, widgets) ->
+                    val (_, isActive) = withPrev
+                    // The validation is performed once the hub becomes active.
+                    if (isActive) {
+                        validateWidgetsAndDeleteOrphaned(widgets)
+                    }
                 }
-            }
-            .launchIn(bgScope)
+                .launchIn(bgScope)
+        }
 
         appWidgetHost.appWidgetIdToRemove
             .onEach { appWidgetId -> communalInteractor.deleteWidget(id = appWidgetId) }
             .launchIn(bgScope)
     }
 
+    private fun onStartInHeadlessSystemUser() {
+        // Connection to the widget manager service in the foreground user should stay open as long
+        // as the Glanceable Hub is available.
+        allOf(
+                communalSettingsInteractor.isCommunalEnabled,
+                not(keyguardInteractor.isEncryptedOrLockdown),
+            )
+            .distinctUntilChanged()
+            // Drop the initial false
+            .dropWhile { !it }
+            .onEach { shouldRegister ->
+                if (shouldRegister) {
+                    glanceableHubWidgetManager.register()
+                } else {
+                    glanceableHubWidgetManager.unregister()
+                }
+            }
+            .launchIn(bgScope)
+    }
+
     private suspend fun updateAppWidgetHostActive(active: Boolean) =
         // Always ensure this is called on the main/ui thread.
         withContext(uiDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
index 0b8d977..8c745f5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
@@ -26,6 +26,8 @@
 import android.os.UserHandle
 import android.widget.RemoteViews
 import androidx.annotation.WorkerThread
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
@@ -38,7 +40,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * Widget host that interacts with AppWidget service and host to bind and provide info for widgets
@@ -52,7 +53,14 @@
     private val appWidgetHost: CommunalAppWidgetHost,
     private val selectedUserInteractor: SelectedUserInteractor,
     @CommunalLog logBuffer: LogBuffer,
+    glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
 ) : CommunalAppWidgetHost.Observer {
+
+    init {
+        // The communal widget host should never be accessed from a headless system user.
+        glanceableHubMultiUserHelper.assertNotInHeadlessSystemUser()
+    }
+
     companion object {
         private const val TAG = "CommunalWidgetHost"
 
@@ -96,7 +104,7 @@
             bindWidget(
                 widgetId = id,
                 user = user ?: UserHandle(selectedUserInteractor.getSelectedUserId()),
-                provider = provider
+                provider = provider,
             )
         ) {
             logger.d("Successfully bound the widget $provider")
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt
index f4962085..a235e7a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt
@@ -20,6 +20,7 @@
 import android.appwidget.AppWidgetManager
 import android.content.Context
 import android.content.res.Resources
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -46,8 +47,15 @@
             @Application context: Context,
             @Background backgroundScope: CoroutineScope,
             @CommunalLog logBuffer: LogBuffer,
+            glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
         ): CommunalAppWidgetHost {
-            return CommunalAppWidgetHost(context, backgroundScope, APP_WIDGET_HOST_ID, logBuffer)
+            return CommunalAppWidgetHost(
+                context,
+                backgroundScope,
+                APP_WIDGET_HOST_ID,
+                logBuffer,
+                glanceableHubMultiUserHelper,
+            )
         }
 
         @SysUISingleton
@@ -58,6 +66,7 @@
             appWidgetHost: CommunalAppWidgetHost,
             selectedUserInteractor: SelectedUserInteractor,
             @CommunalLog logBuffer: LogBuffer,
+            glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
         ): CommunalWidgetHost {
             return CommunalWidgetHost(
                 applicationScope,
@@ -65,6 +74,7 @@
                 appWidgetHost,
                 selectedUserInteractor,
                 logBuffer,
+                glanceableHubMultiUserHelper,
             )
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt
new file mode 100644
index 0000000..202edf7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManager.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.appwidget.AppWidgetHost.AppWidgetHostListener
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.os.IBinder
+import android.os.UserHandle
+import android.widget.RemoteViews
+import com.android.server.servicewatcher.ServiceWatcher
+import com.android.server.servicewatcher.ServiceWatcher.ServiceListener
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IAppWidgetHostListener
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+
+/**
+ * Manages updates to Glanceable Hub widgets and requests to edit them from the headless system
+ * user.
+ *
+ * It communicates with the remote [GlanceableHubWidgetManagerService] which runs in the foreground
+ * user, and abstracts the IPC details from the rest of the system.
+ */
+@SysUISingleton
+class GlanceableHubWidgetManager
+@Inject
+constructor(
+    glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
+    @CommunalLog logBuffer: LogBuffer,
+    serviceWatcherFactory: ServiceWatcherFactory<GlanceableHubWidgetManagerServiceInfo?>,
+) : ServiceListener<GlanceableHubWidgetManagerServiceInfo?> {
+
+    init {
+        // The manager should only be used in the headless system user.
+        glanceableHubMultiUserHelper.assertInHeadlessSystemUser()
+    }
+
+    private val logger = Logger(logBuffer, TAG)
+
+    private val serviceWatcher by lazy { serviceWatcherFactory.create(this) }
+
+    val widgets = conflatedCallbackFlow {
+        val callback =
+            object : IGlanceableHubWidgetsListener.Stub() {
+                override fun onWidgetsUpdated(widgets: List<CommunalWidgetContentModel>?) {
+                    trySend(widgets ?: emptyList())
+                }
+            }
+        runOnService { service -> service.addWidgetsListener(callback) }
+        awaitClose { runOnService { service -> service.removeWidgetsListener(callback) } }
+    }
+
+    fun register() {
+        serviceWatcher.register()
+    }
+
+    fun unregister() {
+        serviceWatcher.unregister()
+    }
+
+    override fun onBind(binder: IBinder?, serviceInfo: GlanceableHubWidgetManagerServiceInfo?) {
+        logger.i("Service bound")
+    }
+
+    override fun onUnbind() {
+        logger.i("Service unbound")
+    }
+
+    /** Requests the foreground user to set a [AppWidgetHostListener] for the given app widget. */
+    fun setAppWidgetHostListener(appWidgetId: Int, listener: AppWidgetHostListener) =
+        runOnService { service ->
+            service.setAppWidgetHostListener(appWidgetId, createIAppWidgetHostListener(listener))
+        }
+
+    /** Requests the foreground user to add a widget. */
+    fun addWidget(
+        provider: ComponentName,
+        user: UserHandle,
+        rank: Int?,
+        configurator: WidgetConfigurator?,
+    ) = runOnService { service ->
+        // TODO(b/375036327): Add support for widget configuration
+        service.addWidget(provider, user, rank ?: -1)
+    }
+
+    /** Requests the foreground user to delete a widget. */
+    fun deleteWidget(appWidgetId: Int) = runOnService { service ->
+        service.deleteWidget(appWidgetId)
+    }
+
+    /** Requests the foreground user to update widget order. */
+    fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) = runOnService { service ->
+        service.updateWidgetOrder(
+            widgetIdToRankMap.keys.toIntArray(),
+            widgetIdToRankMap.values.toIntArray(),
+        )
+    }
+
+    /** Requests the foreground user to resize a widget. */
+    fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) =
+        runOnService { service ->
+            service.resizeWidget(
+                appWidgetId,
+                spanY,
+                widgetIdToRankMap.keys.toIntArray(),
+                widgetIdToRankMap.values.toIntArray(),
+            )
+        }
+
+    private fun runOnService(block: (IGlanceableHubWidgetManagerService) -> Unit) {
+        serviceWatcher.runOnBinder(
+            object : ServiceWatcher.BinderOperation {
+                override fun run(binder: IBinder?) {
+                    block(IGlanceableHubWidgetManagerService.Stub.asInterface(binder))
+                }
+
+                override fun onError(t: Throwable?) {
+                    // TODO(b/375236794): handle failure in case service is unbound
+                }
+            }
+        )
+    }
+
+    private fun createIAppWidgetHostListener(
+        listener: AppWidgetHostListener
+    ): IAppWidgetHostListener {
+        return object : IAppWidgetHostListener.Stub() {
+            override fun onUpdateProviderInfo(appWidget: AppWidgetProviderInfo?) {
+                listener.onUpdateProviderInfo(appWidget)
+            }
+
+            override fun updateAppWidget(views: RemoteViews?) {
+                listener.updateAppWidget(views)
+            }
+
+            override fun updateAppWidgetDeferred(packageName: String?, appWidgetId: Int) {
+                listener.updateAppWidgetDeferred(packageName, appWidgetId)
+            }
+
+            override fun onViewDataChanged(viewId: Int) {
+                listener.onViewDataChanged(viewId)
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "GlanceableHubWidgetManager"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt
new file mode 100644
index 0000000..4d042fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerService.kt
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.appwidget.AppWidgetHost.AppWidgetHostListener
+import android.appwidget.AppWidgetProviderInfo
+import android.content.ComponentName
+import android.content.Intent
+import android.os.IBinder
+import android.os.RemoteCallbackList
+import android.os.UserHandle
+import android.widget.RemoteViews
+import androidx.lifecycle.LifecycleService
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.communal.data.repository.CommunalWidgetRepository
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IAppWidgetHostListener
+import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import javax.inject.Inject
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+/**
+ * Service for the [GlanceableHubWidgetManager], which runs in a foreground user in Headless System
+ * User Mode (HSUM), manages widgets as the user who owns them, and communicates back to the
+ * headless system user, where these widgets are rendered.
+ */
+class GlanceableHubWidgetManagerService
+@Inject
+constructor(
+    private val widgetRepository: CommunalWidgetRepository,
+    private val appWidgetHost: CommunalAppWidgetHost,
+    private val communalWidgetHost: CommunalWidgetHost,
+    glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
+    @CommunalLog logBuffer: LogBuffer,
+) : LifecycleService() {
+
+    init {
+        // The service should only run in a foreground user.
+        glanceableHubMultiUserHelper.assertNotInHeadlessSystemUser()
+    }
+
+    private val logger = Logger(logBuffer, TAG)
+    private val widgetListenersRegistry = WidgetListenerRegistry()
+
+    override fun onCreate() {
+        super.onCreate()
+
+        logger.i("Service created")
+
+        communalWidgetHost.startObservingHost()
+        appWidgetHost.startListening()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+
+        logger.i("Service destroyed")
+
+        appWidgetHost.stopListening()
+        communalWidgetHost.stopObservingHost()
+
+        // Cancel all widget listener jobs and unregister listeners
+        widgetListenersRegistry.kill()
+    }
+
+    override fun onBind(intent: Intent): IBinder? {
+        super.onBind(intent)
+        return WidgetManagerServiceBinder().asBinder()
+    }
+
+    private fun addWidgetsListenerInternal(listener: IGlanceableHubWidgetsListener?) {
+        if (listener == null) {
+            throw IllegalStateException("Listener cannot be null")
+        }
+
+        if (!listener.asBinder().isBinderAlive) {
+            throw IllegalStateException("Listener binder is dead")
+        }
+
+        val job =
+            widgetRepository.communalWidgets
+                .onEach { widgets -> listener.onWidgetsUpdated(widgets) }
+                .launchIn(lifecycleScope)
+        widgetListenersRegistry.register(listener, job)
+    }
+
+    private fun removeWidgetsListenerInternal(listener: IGlanceableHubWidgetsListener?) {
+        if (listener == null) {
+            throw IllegalStateException("Listener cannot be null")
+        }
+
+        widgetListenersRegistry.unregister(listener)
+    }
+
+    private fun setAppWidgetHostListenerInternal(
+        appWidgetId: Int,
+        listener: IAppWidgetHostListener?,
+    ) {
+        if (listener == null) {
+            throw IllegalStateException("Listener cannot be null")
+        }
+
+        appWidgetHost.setListener(appWidgetId, createListener(listener))
+    }
+
+    private fun addWidgetInternal(provider: ComponentName?, user: UserHandle?, rank: Int) {
+        if (provider == null) {
+            throw IllegalStateException("Provider cannot be null")
+        }
+
+        if (user == null) {
+            throw IllegalStateException("User cannot be null")
+        }
+
+        // TODO(b/375036327): Add support for widget configuration
+        widgetRepository.addWidget(provider, user, rank, configurator = null)
+    }
+
+    private fun deleteWidgetInternal(appWidgetId: Int) {
+        widgetRepository.deleteWidget(appWidgetId)
+    }
+
+    private fun updateWidgetOrderInternal(appWidgetIds: IntArray?, ranks: IntArray?) {
+        if (appWidgetIds == null || ranks == null) {
+            throw IllegalStateException("appWidgetIds and ranks cannot be null")
+        }
+
+        if (appWidgetIds.size != ranks.size) {
+            throw IllegalStateException("appWidgetIds and ranks must be the same size")
+        }
+
+        widgetRepository.updateWidgetOrder(appWidgetIds.zip(ranks).toMap())
+    }
+
+    private fun resizeWidgetInternal(
+        appWidgetId: Int,
+        spanY: Int,
+        appWidgetIds: IntArray?,
+        ranks: IntArray?,
+    ) {
+        if (appWidgetIds == null || ranks == null) {
+            throw IllegalStateException("appWidgetIds and ranks cannot be null")
+        }
+
+        if (appWidgetIds.size != ranks.size) {
+            throw IllegalStateException("appWidgetIds and ranks must be the same size")
+        }
+
+        widgetRepository.resizeWidget(appWidgetId, spanY, appWidgetIds.zip(ranks).toMap())
+    }
+
+    private fun createListener(listener: IAppWidgetHostListener): AppWidgetHostListener {
+        return object : AppWidgetHostListener {
+            override fun onUpdateProviderInfo(appWidget: AppWidgetProviderInfo?) {
+                listener.onUpdateProviderInfo(appWidget)
+            }
+
+            override fun updateAppWidget(views: RemoteViews?) {
+                listener.updateAppWidget(views)
+            }
+
+            override fun updateAppWidgetDeferred(packageName: String?, appWidgetId: Int) {
+                listener.updateAppWidgetDeferred(packageName, appWidgetId)
+            }
+
+            override fun onViewDataChanged(viewId: Int) {
+                listener.onViewDataChanged(viewId)
+            }
+        }
+    }
+
+    private inner class WidgetManagerServiceBinder : IGlanceableHubWidgetManagerService.Stub() {
+        override fun addWidgetsListener(listener: IGlanceableHubWidgetsListener?) {
+            val iden = clearCallingIdentity()
+
+            try {
+                addWidgetsListenerInternal(listener)
+            } finally {
+                restoreCallingIdentity(iden)
+            }
+        }
+
+        override fun removeWidgetsListener(listener: IGlanceableHubWidgetsListener?) {
+            val iden = clearCallingIdentity()
+
+            try {
+                removeWidgetsListenerInternal(listener)
+            } finally {
+                restoreCallingIdentity(iden)
+            }
+        }
+
+        override fun setAppWidgetHostListener(appWidgetId: Int, listener: IAppWidgetHostListener?) {
+            val iden = clearCallingIdentity()
+
+            try {
+                setAppWidgetHostListenerInternal(appWidgetId, listener)
+            } finally {
+                restoreCallingIdentity(iden)
+            }
+        }
+
+        override fun addWidget(provider: ComponentName?, user: UserHandle?, rank: Int) {
+            val iden = clearCallingIdentity()
+
+            try {
+                addWidgetInternal(provider, user, rank)
+            } finally {
+                restoreCallingIdentity(iden)
+            }
+        }
+
+        override fun deleteWidget(appWidgetId: Int) {
+            val iden = clearCallingIdentity()
+
+            try {
+                deleteWidgetInternal(appWidgetId)
+            } finally {
+                restoreCallingIdentity(iden)
+            }
+        }
+
+        override fun updateWidgetOrder(appWidgetIds: IntArray?, ranks: IntArray?) {
+            val iden = clearCallingIdentity()
+
+            try {
+                updateWidgetOrderInternal(appWidgetIds, ranks)
+            } finally {
+                restoreCallingIdentity(iden)
+            }
+        }
+
+        override fun resizeWidget(
+            appWidgetId: Int,
+            spanY: Int,
+            appWidgetIds: IntArray?,
+            ranks: IntArray?,
+        ) {
+            val iden = clearCallingIdentity()
+
+            try {
+                resizeWidgetInternal(appWidgetId, spanY, appWidgetIds, ranks)
+            } finally {
+                restoreCallingIdentity(iden)
+            }
+        }
+    }
+
+    /**
+     * Registry of widget listener binders, which handles canceling the job associated with a
+     * listener when it is unregistered, or when the binder is dead.
+     */
+    private class WidgetListenerRegistry : RemoteCallbackList<IGlanceableHubWidgetsListener>() {
+        private val jobs = mutableMapOf<IGlanceableHubWidgetsListener, Job>()
+
+        fun register(listener: IGlanceableHubWidgetsListener, job: Job) {
+            if (register(listener)) {
+                synchronized(jobs) { jobs[listener] = job }
+            } else {
+                job.cancel()
+            }
+        }
+
+        override fun unregister(listener: IGlanceableHubWidgetsListener?): Boolean {
+            synchronized(jobs) { jobs.remove(listener)?.cancel() }
+            return super.unregister(listener)
+        }
+
+        override fun onCallbackDied(listener: IGlanceableHubWidgetsListener?) {
+            synchronized(jobs) { jobs.remove(listener)?.cancel() }
+            super.onCallbackDied(listener)
+        }
+
+        override fun kill() {
+            synchronized(jobs) {
+                jobs.values.forEach { it.cancel() }
+                jobs.clear()
+            }
+            super.kill()
+        }
+    }
+
+    companion object {
+        private const val TAG = "GlanceableHubWidgetManagerService"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceInfo.kt
new file mode 100644
index 0000000..d527611
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceInfo.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.communal.widgets
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.BIND_AUTO_CREATE
+import android.os.UserHandle
+import com.android.server.servicewatcher.ServiceWatcher
+
+/**
+ * Information about the [GlanceableHubWidgetManagerServiceInfo] used for binding with the
+ * [ServiceWatcher].
+ */
+class GlanceableHubWidgetManagerServiceInfo(context: Context, userHandle: UserHandle) :
+    ServiceWatcher.BoundServiceInfo(
+        /* action= */ null,
+        UserHandle.getUid(userHandle.identifier, UserHandle.getCallingAppId()),
+        ComponentName(context.packageName, GlanceableHubWidgetManagerService::class.java.name),
+        BIND_AUTO_CREATE,
+    )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceSupplier.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceSupplier.kt
new file mode 100644
index 0000000..ed77e6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerServiceSupplier.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.content.Context
+import com.android.server.servicewatcher.ServiceWatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Supplies details about the [GlanceableHubWidgetManagerService] that the [ServiceWatcher] should
+ * bind to. Currently the service should only be bound if the current user is the main user.
+ */
+@SysUISingleton
+class GlanceableHubWidgetManagerServiceSupplier
+@Inject
+constructor(
+    @Application private val context: Context,
+    @Background private val bgExecutor: Executor,
+    private val userTracker: UserTracker,
+) : ServiceWatcher.ServiceSupplier<GlanceableHubWidgetManagerServiceInfo?>, UserTracker.Callback {
+
+    private var userAboutToSwitch = false
+    private var listener: ServiceWatcher.ServiceChangedListener? = null
+
+    override fun getServiceInfo(): GlanceableHubWidgetManagerServiceInfo? {
+        return GlanceableHubWidgetManagerServiceInfo(context, userTracker.userHandle)
+    }
+
+    override fun hasMatchingService(): Boolean {
+        // The service becomes unavailable immediately before a user switching is about to happen
+        // so that it is disconnected before the user process is terminated.
+        // It is also only available if the current user is the main user.
+        return !userAboutToSwitch && userTracker.userInfo.isMain
+    }
+
+    override fun register(listener: ServiceWatcher.ServiceChangedListener?) {
+        this.listener = listener
+        userTracker.addCallback(this, bgExecutor)
+    }
+
+    override fun unregister() {
+        listener = null
+        userTracker.removeCallback(this)
+    }
+
+    override fun onBeforeUserSwitching(newUser: Int) {
+        userAboutToSwitch = true
+        listener?.onServiceChanged()
+    }
+
+    override fun onUserChanged(newUser: Int, userContext: Context) {
+        userAboutToSwitch = false
+        listener?.onServiceChanged()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl b/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl
new file mode 100644
index 0000000..e556472
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/IGlanceableHubWidgetManagerService.aidl
@@ -0,0 +1,51 @@
+package com.android.systemui.communal.widgets;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.os.UserHandle;
+import android.widget.RemoteViews;
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel;
+import java.util.List;
+
+// Interface for the [GlanceableHubWidgetManagerService], which runs in a foreground user in HSUM
+// and communicates with the headless system user.
+interface IGlanceableHubWidgetManagerService {
+
+    // Adds a listener for updates of Glanceable Hub widgets.
+    oneway void addWidgetsListener(in IGlanceableHubWidgetsListener listener);
+
+    // Removes a listener for updates of Glanceable Hub widgets.
+    oneway void removeWidgetsListener(in IGlanceableHubWidgetsListener listener);
+
+    // Sets a listener for updates on a specific widget.
+    oneway void setAppWidgetHostListener(int appWidgetId, in IAppWidgetHostListener listener);
+
+    // Requests to add a widget in the Glanceable Hub.
+    oneway void addWidget(in ComponentName provider, in UserHandle user, int rank);
+
+    // Requests to delete a widget from the Glanceable Hub.
+    oneway void deleteWidget(int appWidgetId);
+
+    // Requests to update the order of widgets in the Glanceable Hub.
+    oneway void updateWidgetOrder(in int[] appWidgetIds, in int[] ranks);
+
+    // Requests to resize a widget in the Glanceable Hub.
+    oneway void resizeWidget(int appWidgetId, int spanY, in int[] appWidgetIds, in int[] ranks);
+
+    // Listener for Glanceable Hub widget updates
+    oneway interface IGlanceableHubWidgetsListener {
+        // Called when widgets have updated.
+        void onWidgetsUpdated(in List<CommunalWidgetContentModel> widgets);
+    }
+
+    // Mirrors [AppWidgetHost#AppWidgetHostListener].
+    oneway interface IAppWidgetHostListener {
+        void onUpdateProviderInfo(in @nullable AppWidgetProviderInfo appWidget);
+
+        void updateAppWidget(in @nullable RemoteViews views);
+
+        void updateAppWidgetDeferred(in String packageName, int appWidgetId);
+
+        void onViewDataChanged(int viewId);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/ServiceWatcherFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/ServiceWatcherFactory.kt
new file mode 100644
index 0000000..f79cc87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/ServiceWatcherFactory.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.content.Context
+import android.os.Handler
+import com.android.server.servicewatcher.ServiceWatcher
+import com.android.server.servicewatcher.ServiceWatcher.BoundServiceInfo
+import com.android.server.servicewatcher.ServiceWatcher.ServiceSupplier
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+
+/** Factory for creating a [ServiceWatcher]. */
+interface ServiceWatcherFactory<TBoundService : BoundServiceInfo?> {
+    fun create(listener: ServiceWatcher.ServiceListener<TBoundService?>): ServiceWatcher
+}
+
+/** Implementation of the [ServiceWatcherFactory] for the [GlanceableHubWidgetManagerService]. */
+@SysUISingleton
+class GlanceableHubWidgetManagerServiceWatcherFactoryImpl
+@Inject
+constructor(
+    @Application private val context: Context,
+    @Background private val handler: Handler,
+    private val supplier: ServiceSupplier<GlanceableHubWidgetManagerServiceInfo?>,
+) : ServiceWatcherFactory<GlanceableHubWidgetManagerServiceInfo?> {
+
+    override fun create(
+        listener: ServiceWatcher.ServiceListener<GlanceableHubWidgetManagerServiceInfo?>
+    ): ServiceWatcher {
+        return ServiceWatcher.create(
+            context,
+            handler,
+            GlanceableHubWidgetManagerService::class.java.simpleName,
+            supplier,
+            listener,
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt
index 3e68479..d157cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetConfigurationController.kt
@@ -21,8 +21,10 @@
 import android.content.ActivityNotFoundException
 import android.window.SplashScreen
 import androidx.activity.ComponentActivity
+import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.util.nullableAtomicReference
+import dagger.Lazy
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -38,8 +40,9 @@
 @AssistedInject
 constructor(
     @Assisted private val activity: ComponentActivity,
-    private val appWidgetHost: CommunalAppWidgetHost,
-    @Background private val bgDispatcher: CoroutineDispatcher
+    private val appWidgetHostLazy: Lazy<CommunalAppWidgetHost>,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    private val glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
 ) : WidgetConfigurator {
     @AssistedFactory
     fun interface Factory {
@@ -62,13 +65,21 @@
                 }
 
             try {
-                appWidgetHost.startAppWidgetConfigureActivityForResult(
-                    activity,
-                    appWidgetId,
-                    0,
-                    REQUEST_CODE,
-                    options.toBundle()
-                )
+                // TODO(b/375036327): Add support for widget configuration
+                if (
+                    !glanceableHubMultiUserHelper.glanceableHubHsumFlagEnabled ||
+                        !glanceableHubMultiUserHelper.isInHeadlessSystemUser()
+                ) {
+                    with(appWidgetHostLazy.get()) {
+                        startAppWidgetConfigureActivityForResult(
+                            activity,
+                            appWidgetId,
+                            0,
+                            REQUEST_CODE,
+                            options.toBundle(),
+                        )
+                    }
+                }
             } catch (e: ActivityNotFoundException) {
                 setConfigurationResult(Activity.RESULT_CANCELED)
             }
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/DefaultServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
index 904d5898..8f01775 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
@@ -19,6 +19,7 @@
 import android.app.Service;
 
 import com.android.systemui.SystemUIService;
+import com.android.systemui.communal.widgets.GlanceableHubWidgetManagerService;
 import com.android.systemui.doze.DozeService;
 import com.android.systemui.dreams.DreamOverlayService;
 import com.android.systemui.dump.SystemUIAuxiliaryDumpService;
@@ -93,4 +94,10 @@
     @ClassKey(IssueRecordingService.class)
     public abstract Service bindIssueRecordingService(IssueRecordingService service);
 
+    /** Inject into GlanceableHubWidgetManagerService */
+    @Binds
+    @IntoMap
+    @ClassKey(GlanceableHubWidgetManagerService.class)
+    public abstract Service bindGlanceableHubWidgetManagerService(
+            GlanceableHubWidgetManagerService service);
 }
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 b966ad4..609b733 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -38,6 +38,7 @@
 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;
@@ -53,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;
@@ -68,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;
@@ -120,6 +124,7 @@
         AccessibilityRepositoryModule.class,
         AospPolicyModule.class,
         BatterySaverModule.class,
+        CentralSurfacesModule.class,
         ClipboardOverlaySuppressionModule.class,
         CollapsedStatusBarFragmentStartableModule.class,
         ConnectingDisplayViewModel.StartableModule.class,
@@ -127,6 +132,7 @@
         EmergencyGestureModule.class,
         GestureModule.class,
         HeadsUpModule.class,
+        KeyguardModule.class,
         KeyboardShortcutsModule.class,
         KeyguardBlueprintModule.class,
         KeyguardSectionsModule.class,
@@ -139,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/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 6fb6236..4cdf286 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -29,7 +29,7 @@
 import com.android.systemui.dagger.qualifiers.PerUser
 import com.android.systemui.dreams.AssistantAttentionMonitor
 import com.android.systemui.dreams.DreamMonitor
-import com.android.systemui.dreams.homecontrols.HomeControlsDreamStartable
+import com.android.systemui.dreams.homecontrols.system.HomeControlsDreamStartable
 import com.android.systemui.globalactions.GlobalActionsComponent
 import com.android.systemui.haptics.msdl.MSDLCoreStartable
 import com.android.systemui.keyboard.KeyboardUI
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 59c8f06..cb649f2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -70,6 +70,9 @@
 import com.android.systemui.inputmethod.InputMethodModule;
 import com.android.systemui.keyboard.KeyboardModule;
 import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
+import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule;
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
 import com.android.systemui.keyguard.ui.composable.LockscreenContent;
 import com.android.systemui.log.dagger.LogModule;
 import com.android.systemui.log.dagger.MonitorLog;
@@ -227,6 +230,7 @@
         InputMethodModule.class,
         KeyEventRepositoryModule.class,
         KeyboardModule.class,
+        KeyguardDataQuickAffordanceModule.class,
         LetterboxModule.class,
         LogModule.class,
         MediaProjectionActivitiesModule.class,
@@ -428,6 +432,11 @@
                 sysuiUiBgExecutor));
     }
 
+    @Provides
+    static KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
+        return new KeyguardQuickAffordancesMetricsLoggerImpl();
+    }
+
     @Binds
     abstract FgsManagerController bindFgsManagerController(FgsManagerControllerImpl impl);
 
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
index 782bce4..41a59a9 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
@@ -19,10 +19,12 @@
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.util.kotlin.FlowDumperImpl
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
@@ -46,16 +48,17 @@
 class DeviceEntryHapticsInteractor
 @Inject
 constructor(
-    deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
-    deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
-    deviceEntryBiometricAuthInteractor: DeviceEntryBiometricAuthInteractor,
-    fingerprintPropertyRepository: FingerprintPropertyRepository,
     biometricSettingsRepository: BiometricSettingsRepository,
+    deviceEntryBiometricAuthInteractor: DeviceEntryBiometricAuthInteractor,
+    deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
+    deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
+    fingerprintPropertyRepository: FingerprintPropertyRepository,
     keyEventInteractor: KeyEventInteractor,
+    private val logger: BiometricUnlockLogger,
     powerInteractor: PowerInteractor,
     private val systemClock: SystemClock,
-    private val logger: BiometricUnlockLogger,
-) {
+    dumpManager: DumpManager,
+) : FlowDumperImpl(dumpManager) {
     private val powerButtonSideFpsEnrolled =
         combineTransform(
                 fingerprintPropertyRepository.sensorType,
@@ -86,7 +89,7 @@
                     powerButtonSideFpsEnrolled,
                     powerButtonDown,
                     lastPowerButtonWakeup,
-                    ::Triple
+                    ::Triple,
                 )
             )
             .filter { (sideFpsEnrolled, powerButtonDown, lastPowerButtonWakeup) ->
@@ -100,14 +103,19 @@
                 }
                 allowHaptic
             }
-            .map {} // map to Unit
+            // map to Unit
+            .map {}
+            .dumpWhileCollecting("playSuccessHaptic")
 
     private val playErrorHapticForBiometricFailure: Flow<Unit> =
         merge(
                 deviceEntryFingerprintAuthInteractor.fingerprintFailure,
                 deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure,
             )
-            .map {} // map to Unit
+            // map to Unit
+            .map {}
+            .dumpWhileCollecting("playErrorHapticForBiometricFailure")
+
     val playErrorHaptic: Flow<Unit> =
         playErrorHapticForBiometricFailure
             .sample(combine(powerButtonSideFpsEnrolled, powerButtonDown, ::Pair))
@@ -118,7 +126,9 @@
                 }
                 allowHaptic
             }
-            .map {} // map to Unit
+            // map to Unit
+            .map {}
+            .dumpWhileCollecting("playErrorHaptic")
 
     private val recentPowerButtonPressThresholdMs = 400L
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt
index 0b9336f..b2d4405 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractor.kt
@@ -16,18 +16,49 @@
 
 package com.android.systemui.deviceentry.domain.interactor
 
+import androidx.annotation.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.KeyguardBypassInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_ONLY_WAKE
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_SHOW_BOUNCER
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
+import com.android.systemui.statusbar.phone.DozeScrimController
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
+import com.android.systemui.util.kotlin.combine
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
 
 /**
  * Hosts application business logic related to the source of the user entering the device. Note: The
@@ -42,13 +73,184 @@
 class DeviceEntrySourceInteractor
 @Inject
 constructor(
+    authenticationInteractor: AuthenticationInteractor,
+    authController: AuthController,
+    alternateBouncerInteractor: AlternateBouncerInteractor,
+    deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+    deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
+    dozeScrimController: DozeScrimController,
+    keyguardBypassInteractor: KeyguardBypassInteractor,
+    keyguardUpdateMonitor: KeyguardUpdateMonitor,
     keyguardInteractor: KeyguardInteractor,
-) {
+    sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor,
+    sceneInteractor: SceneInteractor,
+    dumpManager: DumpManager,
+) : FlowDumperImpl(dumpManager) {
+    private val isShowingBouncerScene: Flow<Boolean> =
+        sceneInteractor.transitionState
+            .map { transitionState ->
+                transitionState.isIdle(Scenes.Bouncer) ||
+                    transitionState.isTransitioning(null, Scenes.Bouncer)
+            }
+            .distinctUntilChanged()
+            .dumpWhileCollecting("isShowingBouncerScene")
+
+    private val isUnlockedWithStrongFaceUnlock =
+        deviceEntryFaceAuthInteractor.authenticationStatus
+            .map { status ->
+                (status as? SuccessFaceAuthenticationStatus)?.successResult?.isStrongBiometric
+                    ?: false
+            }
+            .dumpWhileCollecting("unlockedWithStrongFaceUnlock")
+
+    private val isUnlockedWithStrongFingerprintUnlock =
+        deviceEntryFingerprintAuthInteractor.authenticationStatus
+            .map { status ->
+                (status as? SuccessFingerprintAuthenticationStatus)?.isStrongBiometric ?: false
+            }
+            .dumpWhileCollecting("unlockedWithStrongFingerprintUnlock")
+
+    private val faceWakeAndUnlockMode: Flow<BiometricUnlockMode> =
+        combine(
+                alternateBouncerInteractor.isVisible,
+                keyguardBypassInteractor.isBypassAvailable,
+                isUnlockedWithStrongFaceUnlock,
+                sceneContainerOcclusionInteractor.isOccludingActivityShown,
+                sceneInteractor.currentScene,
+                isShowingBouncerScene,
+            ) {
+                isAlternateBouncerVisible,
+                isBypassAvailable,
+                isFaceStrongBiometric,
+                isOccluded,
+                currentScene,
+                isShowingBouncerScene ->
+                val isUnlockingAllowed =
+                    keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(isFaceStrongBiometric)
+                val bypass = isBypassAvailable || authController.isUdfpsFingerDown()
+
+                when {
+                    !keyguardUpdateMonitor.isDeviceInteractive ->
+                        when {
+                            !isUnlockingAllowed -> if (bypass) MODE_SHOW_BOUNCER else MODE_NONE
+                            bypass -> MODE_WAKE_AND_UNLOCK_PULSING
+                            else -> MODE_ONLY_WAKE
+                        }
+
+                    isUnlockingAllowed && currentScene == Scenes.Dream ->
+                        if (bypass) MODE_WAKE_AND_UNLOCK_FROM_DREAM else MODE_ONLY_WAKE
+
+                    isUnlockingAllowed && isOccluded -> MODE_UNLOCK_COLLAPSING
+
+                    (isShowingBouncerScene || isAlternateBouncerVisible) && isUnlockingAllowed ->
+                        MODE_DISMISS_BOUNCER
+
+                    isUnlockingAllowed && bypass -> MODE_UNLOCK_COLLAPSING
+
+                    bypass -> MODE_SHOW_BOUNCER
+
+                    else -> MODE_NONE
+                }
+            }
+            .map { biometricModeIntToObject(it) }
+            .dumpWhileCollecting("faceWakeAndUnlockMode")
+
+    private val fingerprintWakeAndUnlockMode: Flow<BiometricUnlockMode> =
+        combine(
+                alternateBouncerInteractor.isVisible,
+                authenticationInteractor.authenticationMethod,
+                sceneInteractor.currentScene,
+                isUnlockedWithStrongFingerprintUnlock,
+                isShowingBouncerScene,
+            ) {
+                alternateBouncerVisible,
+                authenticationMethod,
+                currentScene,
+                isFingerprintStrongBiometric,
+                isShowingBouncerScene ->
+                val unlockingAllowed =
+                    keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                        isFingerprintStrongBiometric
+                    )
+                when {
+                    !keyguardUpdateMonitor.isDeviceInteractive ->
+                        when {
+                            dozeScrimController.isPulsing && unlockingAllowed ->
+                                MODE_WAKE_AND_UNLOCK_PULSING
+
+                            unlockingAllowed || !authenticationMethod.isSecure ->
+                                MODE_WAKE_AND_UNLOCK
+
+                            else -> MODE_SHOW_BOUNCER
+                        }
+
+                    unlockingAllowed && currentScene == Scenes.Dream ->
+                        MODE_WAKE_AND_UNLOCK_FROM_DREAM
+
+                    isShowingBouncerScene && unlockingAllowed -> MODE_DISMISS_BOUNCER
+
+                    unlockingAllowed -> MODE_UNLOCK_COLLAPSING
+
+                    currentScene != Scenes.Bouncer && !alternateBouncerVisible -> MODE_SHOW_BOUNCER
+
+                    else -> MODE_NONE
+                }
+            }
+            .map { biometricModeIntToObject(it) }
+            .dumpWhileCollecting("fingerprintWakeAndUnlockMode")
+
+    @VisibleForTesting
+    private val biometricUnlockStateOnKeyguardDismissed =
+        merge(
+                fingerprintWakeAndUnlockMode
+                    .filter { BiometricUnlockMode.dismissesKeyguard(it) }
+                    .map { mode ->
+                        BiometricUnlockModel(mode, BiometricUnlockSource.FINGERPRINT_SENSOR)
+                    },
+                faceWakeAndUnlockMode
+                    .filter { BiometricUnlockMode.dismissesKeyguard(it) }
+                    .map { mode -> BiometricUnlockModel(mode, BiometricUnlockSource.FACE_SENSOR) },
+            )
+            .dumpWhileCollecting("biometricUnlockState")
+
+    private val deviceEntryFingerprintAuthWakeAndUnlockEvents:
+        Flow<SuccessFingerprintAuthenticationStatus> =
+        deviceEntryFingerprintAuthInteractor.authenticationStatus
+            .filterIsInstance<SuccessFingerprintAuthenticationStatus>()
+            .dumpWhileCollecting("deviceEntryFingerprintAuthSuccessEvents")
+
+    private val deviceEntryFaceAuthWakeAndUnlockEvents: Flow<SuccessFaceAuthenticationStatus> =
+        deviceEntryFaceAuthInteractor.authenticationStatus
+            .filterIsInstance<SuccessFaceAuthenticationStatus>()
+            .sampleFilter(
+                combine(
+                    sceneContainerOcclusionInteractor.isOccludingActivityShown,
+                    keyguardBypassInteractor.isBypassAvailable,
+                    keyguardBypassInteractor.canBypass,
+                    ::Triple,
+                )
+            ) { (isOccludingActivityShown, isBypassAvailable, canBypass) ->
+                isOccludingActivityShown || !isBypassAvailable || canBypass
+            }
+            .dumpWhileCollecting("deviceEntryFaceAuthSuccessEvents")
+
+    private val deviceEntryBiometricAuthSuccessEvents =
+        merge(deviceEntryFingerprintAuthWakeAndUnlockEvents, deviceEntryFaceAuthWakeAndUnlockEvents)
+            .dumpWhileCollecting("deviceEntryBiometricAuthSuccessEvents")
+
     val deviceEntryFromBiometricSource: Flow<BiometricUnlockSource> =
-        keyguardInteractor.biometricUnlockState
-            .filter { BiometricUnlockMode.dismissesKeyguard(it.mode) }
-            .map { it.source }
-            .filterNotNull()
+        if (SceneContainerFlag.isEnabled) {
+                deviceEntryBiometricAuthSuccessEvents
+                    .sample(biometricUnlockStateOnKeyguardDismissed)
+                    .map { it.source }
+                    .filterNotNull()
+            } else {
+                keyguardInteractor.biometricUnlockState
+                    .filter { BiometricUnlockMode.dismissesKeyguard(it.mode) }
+                    .map { it.source }
+                    .filterNotNull()
+            }
+            .dumpWhileCollecting("deviceEntryFromBiometricSource")
 
     private val attemptEnterDeviceFromDeviceEntryIcon: MutableSharedFlow<Unit> = MutableSharedFlow()
     val deviceEntryFromDeviceEntryIcon: Flow<Unit> =
@@ -60,4 +262,18 @@
     suspend fun attemptEnterDeviceFromDeviceEntryIcon() {
         attemptEnterDeviceFromDeviceEntryIcon.emit(Unit)
     }
+
+    private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockMode {
+        return when (value) {
+            MODE_NONE -> BiometricUnlockMode.NONE
+            MODE_WAKE_AND_UNLOCK -> BiometricUnlockMode.WAKE_AND_UNLOCK
+            MODE_WAKE_AND_UNLOCK_PULSING -> BiometricUnlockMode.WAKE_AND_UNLOCK_PULSING
+            MODE_SHOW_BOUNCER -> BiometricUnlockMode.SHOW_BOUNCER
+            MODE_ONLY_WAKE -> BiometricUnlockMode.ONLY_WAKE
+            MODE_UNLOCK_COLLAPSING -> BiometricUnlockMode.UNLOCK_COLLAPSING
+            MODE_WAKE_AND_UNLOCK_FROM_DREAM -> BiometricUnlockMode.WAKE_AND_UNLOCK_FROM_DREAM
+            MODE_DISMISS_BOUNCER -> BiometricUnlockMode.DISMISS_BOUNCER
+            else -> throw IllegalArgumentException("Invalid BiometricUnlockModel value: $value")
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt
index a20556c..15631f8 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/OccludingAppDeviceEntryInteractor.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.content.Intent
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -34,6 +35,7 @@
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.res.R
 import com.android.systemui.util.kotlin.combine
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
@@ -48,7 +50,6 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Business logic for handling authentication events when an app is occluding the lockscreen. */
 @ExperimentalCoroutinesApi
@@ -123,19 +124,28 @@
             .ifKeyguardOccludedByApp(/* elseFlow */ flowOf(null))
 
     init {
-        scope.launch {
-            // On fingerprint success when the screen is on and not dreaming, go to the home screen
-            fingerprintUnlockSuccessEvents
-                .sample(
-                    combine(powerInteractor.isInteractive, keyguardInteractor.isDreaming, ::Pair)
-                )
-                .collect { (interactive, dreaming) ->
-                    if (interactive && !dreaming) {
-                        goToHomeScreen()
+        // This seems undesirable in most cases, except when a video is playing and can PiP when
+        // unlocked. It was originally added for tablets, so allow it there
+        if (context.resources.getBoolean(R.bool.config_goToHomeFromOccludedApps)) {
+            scope.launch {
+                // On fingerprint success when the screen is on and not dreaming, go to the home
+                // screen
+                fingerprintUnlockSuccessEvents
+                    .sample(
+                        combine(
+                            powerInteractor.isInteractive,
+                            keyguardInteractor.isDreaming,
+                            ::Pair,
+                        )
+                    )
+                    .collect { (interactive, dreaming) ->
+                        if (interactive && !dreaming) {
+                            goToHomeScreen()
+                        }
+                        // don't go to the home screen if the authentication is from
+                        // AOD/dozing/off/dreaming
                     }
-                    // don't go to the home screen if the authentication is from
-                    // AOD/dozing/off/dreaming
-                }
+            }
         }
 
         scope.launch {
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/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index a45ad15..3171bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -33,9 +33,10 @@
 import com.android.systemui.dreams.DreamOverlayService;
 import com.android.systemui.dreams.SystemDialogsCloser;
 import com.android.systemui.dreams.complication.dagger.ComplicationComponent;
-import com.android.systemui.dreams.homecontrols.DreamServiceDelegate;
-import com.android.systemui.dreams.homecontrols.DreamServiceDelegateImpl;
 import com.android.systemui.dreams.homecontrols.HomeControlsDreamService;
+import com.android.systemui.dreams.homecontrols.dagger.HomeControlsDataSourceModule;
+import com.android.systemui.dreams.homecontrols.dagger.HomeControlsRemoteServiceComponent;
+import com.android.systemui.dreams.homecontrols.system.HomeControlsRemoteService;
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.pipeline.shared.TileSpec;
 import com.android.systemui.qs.shared.model.TileCategory;
@@ -61,13 +62,15 @@
  * Dagger Module providing Dream-related functionality.
  */
 @Module(includes = {
-            RegisteredComplicationsModule.class,
-            LowLightDreamModule.class,
-            ScrimModule.class
-        },
+        RegisteredComplicationsModule.class,
+        LowLightDreamModule.class,
+        ScrimModule.class,
+        HomeControlsDataSourceModule.class,
+},
         subcomponents = {
-            ComplicationComponent.class,
-            DreamOverlayComponent.class,
+                ComplicationComponent.class,
+                DreamOverlayComponent.class,
+                HomeControlsRemoteServiceComponent.class,
         })
 public interface DreamModule {
     String DREAM_ONLY_ENABLED_FOR_DOCK_USER = "dream_only_enabled_for_dock_user";
@@ -113,6 +116,15 @@
             HomeControlsDreamService service);
 
     /**
+     * Provides Home Controls Remote Service
+     */
+    @Binds
+    @IntoMap
+    @ClassKey(HomeControlsRemoteService.class)
+    Service bindHomeControlsRemoteService(
+            HomeControlsRemoteService service);
+
+    /**
      * Provides a touch inset manager for dreams.
      */
     @Provides
@@ -202,10 +214,4 @@
                 QSTilePolicy.NoRestrictions.INSTANCE
                 );
     }
-
-
-    /** Provides delegate to allow for testing of dream service */
-    @Binds
-    DreamServiceDelegate bindDreamDelegate(DreamServiceDelegateImpl impl);
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegate.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegate.kt
deleted file mode 100644
index 2cfb02e..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegate.kt
+++ /dev/null
@@ -1,34 +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.dreams.homecontrols
-
-import android.app.Activity
-import android.service.dreams.DreamService
-
-/** Provides abstraction for [DreamService] methods, so they can be mocked in tests. */
-interface DreamServiceDelegate {
-    /** Wrapper for [DreamService.getActivity] which can be mocked in tests. */
-    fun getActivity(dreamService: DreamService): Activity?
-
-    /** Wrapper for [DreamService.wakeUp] which can be mocked in tests. */
-    fun wakeUp(dreamService: DreamService)
-
-    /** Wrapper for [DreamService.finish] which can be mocked in tests. */
-    fun finish(dreamService: DreamService)
-
-    /** Wrapper for [DreamService.getRedirectWake] which can be mocked in tests. */
-    fun redirectWake(dreamService: DreamService): Boolean
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegateImpl.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegateImpl.kt
deleted file mode 100644
index 7dc5434..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegateImpl.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.dreams.homecontrols
-
-import android.app.Activity
-import android.service.dreams.DreamService
-import javax.inject.Inject
-
-class DreamServiceDelegateImpl @Inject constructor() : DreamServiceDelegate {
-    override fun getActivity(dreamService: DreamService): Activity {
-        return dreamService.activity
-    }
-
-    override fun finish(dreamService: DreamService) {
-        dreamService.finish()
-    }
-
-    override fun wakeUp(dreamService: DreamService) {
-        dreamService.wakeUp()
-    }
-
-    override fun redirectWake(dreamService: DreamService): Boolean {
-        return dreamService.redirectWake
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
index 6b14ebf..9eec1b6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
@@ -16,75 +16,109 @@
 
 package com.android.systemui.dreams.homecontrols
 
+import android.annotation.SuppressLint
 import android.content.Intent
 import android.os.PowerManager
 import android.service.controls.ControlsProviderService
 import android.service.dreams.DreamService
 import android.window.TaskFragmentInfo
-import com.android.systemui.controls.settings.ControlsSettingsRepository
-import com.android.systemui.coroutines.newTracingContext
-import com.android.systemui.dagger.qualifiers.Background
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.ServiceLifecycleDispatcher
+import androidx.lifecycle.lifecycleScope
 import com.android.systemui.dreams.DreamLogger
-import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
-import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor.Companion.MAX_UPDATE_CORRELATION_DELAY
+import com.android.systemui.dreams.homecontrols.service.TaskFragmentComponent
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsDataSource
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.dagger.DreamLog
+import com.android.systemui.util.time.SystemClock
 import com.android.systemui.util.wakelock.WakeLock
-import com.android.systemui.util.wakelock.WakeLock.Builder.NO_TIMEOUT
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancel
 import kotlinx.coroutines.delay
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
 
+/**
+ * [DreamService] which embeds the user's chosen home controls app to allow it to display as a
+ * screensaver. This service will run in the foreground user context.
+ */
 class HomeControlsDreamService
 @Inject
-constructor(
-    private val controlsSettingsRepository: ControlsSettingsRepository,
-    private val taskFragmentFactory: TaskFragmentComponent.Factory,
-    private val homeControlsComponentInteractor: HomeControlsComponentInteractor,
-    private val wakeLockBuilder: WakeLock.Builder,
-    private val dreamServiceDelegate: DreamServiceDelegate,
-    @Background private val bgDispatcher: CoroutineDispatcher,
-    @DreamLog logBuffer: LogBuffer
-) : DreamService() {
+constructor(private val factory: HomeControlsDreamServiceImpl.Factory) :
+    DreamService(), LifecycleOwner {
 
-    private val serviceJob = SupervisorJob()
-    private val serviceScope =
-        CoroutineScope(bgDispatcher + serviceJob + newTracingContext("HomeControlsDreamService"))
+    private val dispatcher = ServiceLifecycleDispatcher(this)
+    override val lifecycle: Lifecycle
+        get() = dispatcher.lifecycle
+
+    private val impl: HomeControlsDreamServiceImpl by lazy { factory.create(this, this) }
+
+    override fun onCreate() {
+        dispatcher.onServicePreSuperOnCreate()
+        super.onCreate()
+    }
+
+    override fun onDreamingStarted() {
+        dispatcher.onServicePreSuperOnStart()
+        super.onDreamingStarted()
+    }
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+        impl.onAttachedToWindow()
+    }
+
+    override fun onDetachedFromWindow() {
+        dispatcher.onServicePreSuperOnDestroy()
+        super.onDetachedFromWindow()
+        impl.onDetachedFromWindow()
+    }
+}
+
+/**
+ * Implementation of the home controls dream service, which allows for injecting a [DreamService]
+ * and [LifecycleOwner] for testing.
+ */
+class HomeControlsDreamServiceImpl
+@AssistedInject
+constructor(
+    private val taskFragmentFactory: TaskFragmentComponent.Factory,
+    private val wakeLockBuilder: WakeLock.Builder,
+    private val powerManager: PowerManager,
+    private val systemClock: SystemClock,
+    private val dataSource: HomeControlsDataSource,
+    @DreamLog logBuffer: LogBuffer,
+    @Assisted private val service: DreamService,
+    @Assisted lifecycleOwner: LifecycleOwner,
+) : LifecycleOwner by lifecycleOwner {
+
     private val logger = DreamLogger(logBuffer, TAG)
     private lateinit var taskFragmentComponent: TaskFragmentComponent
     private val wakeLock: WakeLock by lazy {
         wakeLockBuilder
-            .setMaxTimeout(NO_TIMEOUT)
+            .setMaxTimeout(WakeLock.Builder.NO_TIMEOUT)
             .setTag(TAG)
             .setLevelsAndFlags(PowerManager.SCREEN_BRIGHT_WAKE_LOCK)
             .build()
     }
 
-    override fun onAttachedToWindow() {
-        super.onAttachedToWindow()
-        val activity = dreamServiceDelegate.getActivity(this)
+    fun onAttachedToWindow() {
+        val activity = service.activity
         if (activity == null) {
-            finish()
+            service.finish()
             return
         }
-
-        // Start monitoring package updates to possibly restart the dream if the home controls
-        // package is updated while we are dreaming.
-        serviceScope.launch { homeControlsComponentInteractor.monitorUpdatesAndRestart() }
-
         taskFragmentComponent =
             taskFragmentFactory
                 .create(
                     activity = activity,
                     onCreateCallback = { launchActivity() },
                     onInfoChangedCallback = this::onTaskFragmentInfoChanged,
-                    hide = { endDream(false) }
+                    hide = { endDream(false) },
                 )
                 .apply { createTaskFragment() }
 
@@ -99,53 +133,61 @@
     }
 
     private fun endDream(handleRedirect: Boolean) {
-        homeControlsComponentInteractor.onDreamEndUnexpectedly()
-        if (handleRedirect && dreamServiceDelegate.redirectWake(this)) {
-            dreamServiceDelegate.wakeUp(this)
-            serviceScope.launch {
+        pokeUserActivity()
+        if (handleRedirect && service.redirectWake) {
+            service.wakeUp()
+            lifecycleScope.launch {
                 delay(ACTIVITY_RESTART_DELAY)
                 launchActivity()
             }
         } else {
-            dreamServiceDelegate.finish(this)
+            service.finish()
         }
     }
 
     private fun launchActivity() {
-        val setting = controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen.value
-        val componentName = homeControlsComponentInteractor.panelComponent.value
-        logger.d("Starting embedding $componentName")
-        val intent =
-            Intent().apply {
-                component = componentName
-                putExtra(ControlsProviderService.EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, setting)
-                putExtra(
-                    ControlsProviderService.EXTRA_CONTROLS_SURFACE,
-                    ControlsProviderService.CONTROLS_SURFACE_DREAM
-                )
-            }
-        taskFragmentComponent.startActivityInTaskFragment(intent)
-    }
-
-    override fun onDetachedFromWindow() {
-        super.onDetachedFromWindow()
-        wakeLock.release(TAG)
-        taskFragmentComponent.destroy()
-        serviceScope.launch {
-            delay(CANCELLATION_DELAY_AFTER_DETACHED)
-            serviceJob.cancel("Dream detached from window")
+        lifecycleScope.launch {
+            val (componentName, setting) = dataSource.componentInfo.first()
+            logger.d("Starting embedding $componentName")
+            val intent =
+                Intent().apply {
+                    component = componentName
+                    putExtra(
+                        ControlsProviderService.EXTRA_LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
+                        setting,
+                    )
+                    putExtra(
+                        ControlsProviderService.EXTRA_CONTROLS_SURFACE,
+                        ControlsProviderService.CONTROLS_SURFACE_DREAM,
+                    )
+                }
+            taskFragmentComponent.startActivityInTaskFragment(intent)
         }
     }
 
-    private companion object {
-        /**
-         * Defines how long after the dream ends that we should keep monitoring for package updates
-         * to attempt a restart of the dream. This should be larger than
-         * [MAX_UPDATE_CORRELATION_DELAY] as it also includes the time the package update takes to
-         * complete.
-         */
-        val CANCELLATION_DELAY_AFTER_DETACHED = 5.seconds
+    fun onDetachedFromWindow() {
+        wakeLock.release(TAG)
+        taskFragmentComponent.destroy()
+    }
 
+    @SuppressLint("MissingPermission")
+    private fun pokeUserActivity() {
+        powerManager.userActivity(
+            systemClock.uptimeMillis(),
+            PowerManager.USER_ACTIVITY_EVENT_OTHER,
+            PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
+        )
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            service: DreamService,
+            lifecycleOwner: LifecycleOwner,
+        ): HomeControlsDreamServiceImpl
+    }
+
+    companion object {
         /**
          * Defines the delay after wakeup where we should attempt to restart the embedded activity.
          * When a wakeup is redirected, the dream service may keep running. In this case, we should
@@ -153,6 +195,6 @@
          * after the wakeup transition has played.
          */
         val ACTIVITY_RESTART_DELAY = 334.milliseconds
-        const val TAG = "HomeControlsDreamService"
+        private const val TAG = "HomeControlsDreamServiceImpl"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsDataSourceModule.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsDataSourceModule.kt
new file mode 100644
index 0000000..3a2791f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsDataSourceModule.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.dreams.homecontrols.dagger
+
+import com.android.systemui.Flags.homeControlsDreamHsum
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dreams.homecontrols.service.RemoteHomeControlsDataSourceDelegator
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsDataSource
+import com.android.systemui.dreams.homecontrols.system.LocalHomeControlsDataSourceDelegator
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface HomeControlsDataSourceModule {
+    companion object {
+        @Provides
+        @SysUISingleton
+        fun providesHomeControlsDataSource(
+            localSource: Lazy<LocalHomeControlsDataSourceDelegator>,
+            remoteSource: Lazy<RemoteHomeControlsDataSourceDelegator>,
+        ): HomeControlsDataSource {
+            return if (homeControlsDreamHsum()) {
+                remoteSource.get()
+            } else {
+                localSource.get()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsRemoteServiceComponent.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsRemoteServiceComponent.kt
new file mode 100644
index 0000000..500d15e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/dagger/HomeControlsRemoteServiceComponent.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.systemui.dreams.homecontrols.dagger
+
+import android.content.Context
+import android.content.Intent
+import android.os.IBinder
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dreams.homecontrols.service.HomeControlsRemoteProxy
+import com.android.systemui.dreams.homecontrols.shared.IHomeControlsRemoteProxy
+import com.android.systemui.dreams.homecontrols.system.HomeControlsRemoteService
+import com.android.systemui.util.service.ObservableServiceConnection
+import com.android.systemui.util.service.Observer
+import com.android.systemui.util.service.PersistentConnectionManager
+import com.android.systemui.util.service.dagger.ObservableServiceModule
+import dagger.BindsInstance
+import dagger.Module
+import dagger.Provides
+import dagger.Subcomponent
+import javax.inject.Named
+
+/**
+ * This component is responsible for generating the connection to the home controls remote service
+ * which runs in the SYSTEM_USER context and provides the data needed to run the home controls dream
+ * in the foreground user context.
+ */
+@Subcomponent(
+    modules =
+        [
+            ObservableServiceModule::class,
+            HomeControlsRemoteServiceComponent.HomeControlsRemoteServiceModule::class,
+        ]
+)
+interface HomeControlsRemoteServiceComponent {
+    /** Creates a [HomeControlsRemoteServiceComponent]. */
+    @Subcomponent.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance callback: ObservableServiceConnection.Callback<HomeControlsRemoteProxy>
+        ): HomeControlsRemoteServiceComponent
+    }
+
+    /** A [PersistentConnectionManager] pointing to the home controls remote service. */
+    val connectionManager: PersistentConnectionManager<HomeControlsRemoteProxy>
+
+    /** Scoped module providing specific components for the [ObservableServiceConnection]. */
+    @Module
+    interface HomeControlsRemoteServiceModule {
+        companion object {
+            @Provides
+            @Named(ObservableServiceModule.SERVICE_CONNECTION)
+            fun providesConnection(
+                connection: ObservableServiceConnection<HomeControlsRemoteProxy>,
+                callback: ObservableServiceConnection.Callback<HomeControlsRemoteProxy>,
+            ): ObservableServiceConnection<HomeControlsRemoteProxy> {
+                connection.addCallback(callback)
+                return connection
+            }
+
+            /** Provides the wrapper around the home controls remote binder */
+            @Provides
+            fun providesTransformer(
+                factory: HomeControlsRemoteProxy.Factory
+            ): ObservableServiceConnection.ServiceTransformer<HomeControlsRemoteProxy> {
+                return ObservableServiceConnection.ServiceTransformer { service: IBinder ->
+                    factory.create(IHomeControlsRemoteProxy.Stub.asInterface(service))
+                }
+            }
+
+            /** Provides the intent to connect to [HomeControlsRemoteService] */
+            @Provides
+            fun providesIntent(@Application context: Context): Intent {
+                return Intent(context, HomeControlsRemoteService::class.java)
+            }
+
+            /** Provides no-op [Observer] since the remote service is in the same package */
+            @Provides
+            @Named(ObservableServiceModule.OBSERVER)
+            fun providesObserver(): Observer {
+                return object : Observer {
+                    override fun addCallback(callback: Observer.Callback?) {
+                        // no-op, do nothing
+                    }
+
+                    override fun removeCallback(callback: Observer.Callback?) {
+                        // no-op, do nothing
+                    }
+                }
+            }
+
+            /**
+             * Provides a name that will be used by [PersistentConnectionManager] when logging
+             * state.
+             */
+            @Provides
+            @Named(ObservableServiceModule.DUMPSYS_NAME)
+            fun providesDumpsysName(): String {
+                return "HomeControlsRemoteService"
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/domain/interactor/HomeControlsComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/domain/interactor/HomeControlsComponentInteractor.kt
deleted file mode 100644
index 2034138..0000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/domain/interactor/HomeControlsComponentInteractor.kt
+++ /dev/null
@@ -1,200 +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.dreams.homecontrols.domain.interactor
-
-import android.annotation.SuppressLint
-import android.app.DreamManager
-import android.content.ComponentName
-import android.os.PowerManager
-import android.os.UserHandle
-import com.android.systemui.common.domain.interactor.PackageChangeInteractor
-import com.android.systemui.common.shared.model.PackageChangeModel
-import com.android.systemui.controls.ControlsServiceInfo
-import com.android.systemui.controls.dagger.ControlsComponent
-import com.android.systemui.controls.management.ControlsListingController
-import com.android.systemui.controls.panels.AuthorizedPanelsRepository
-import com.android.systemui.controls.panels.SelectedComponentRepository
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.kotlin.getOrNull
-import com.android.systemui.util.kotlin.pairwiseBy
-import com.android.systemui.util.kotlin.sample
-import com.android.systemui.util.time.SystemClock
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
-import javax.inject.Inject
-import kotlin.math.abs
-import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.stateIn
-
-@SysUISingleton
-@OptIn(ExperimentalCoroutinesApi::class)
-class HomeControlsComponentInteractor
-@Inject
-constructor(
-    private val selectedComponentRepository: SelectedComponentRepository,
-    controlsComponent: ControlsComponent,
-    authorizedPanelsRepository: AuthorizedPanelsRepository,
-    userRepository: UserRepository,
-    private val packageChangeInteractor: PackageChangeInteractor,
-    private val systemClock: SystemClock,
-    private val powerManager: PowerManager,
-    private val dreamManager: DreamManager,
-    @Background private val bgScope: CoroutineScope
-) {
-    private val controlsListingController: ControlsListingController? =
-        controlsComponent.getControlsListingController().getOrNull()
-
-    /** Gets the current user's selected panel, or null if there isn't one */
-    private val selectedPanel: Flow<SelectedComponentRepository.SelectedComponent?> =
-        userRepository.selectedUserInfo
-            .flatMapLatest { user ->
-                selectedComponentRepository.selectedComponentFlow(user.userHandle)
-            }
-            .map { if (it?.isPanel == true) it else null }
-
-    /** Gets the current user's authorized panels */
-    private val allAuthorizedPanels: Flow<Set<String>> =
-        userRepository.selectedUserInfo.flatMapLatest { user ->
-            authorizedPanelsRepository.observeAuthorizedPanels(user.userHandle)
-        }
-
-    /** Gets all the available services from [ControlsListingController] */
-    private fun allAvailableServices(): Flow<List<ControlsServiceInfo>> {
-        if (controlsListingController == null) {
-            return emptyFlow()
-        }
-        return conflatedCallbackFlow {
-                val listener =
-                    object : ControlsListingController.ControlsListingCallback {
-                        override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
-                            trySend(serviceInfos)
-                        }
-                    }
-                controlsListingController.addCallback(listener)
-                awaitClose { controlsListingController.removeCallback(listener) }
-            }
-            .onStart { emit(controlsListingController.getCurrentServices()) }
-    }
-
-    /** Gets all panels which are available and authorized by the user */
-    private val allAvailableAndAuthorizedPanels: Flow<List<PanelComponent>> =
-        combine(
-            allAvailableServices(),
-            allAuthorizedPanels,
-        ) { serviceInfos, authorizedPanels ->
-            serviceInfos.mapNotNull {
-                val panelActivity = it.panelActivity
-                if (it.componentName.packageName in authorizedPanels && panelActivity != null) {
-                    PanelComponent(it.componentName, panelActivity)
-                } else {
-                    null
-                }
-            }
-        }
-
-    val panelComponent: StateFlow<ComponentName?> =
-        combine(
-                allAvailableAndAuthorizedPanels,
-                selectedPanel,
-            ) { panels, selected ->
-                val item =
-                    panels.firstOrNull { it.componentName == selected?.componentName }
-                        ?: panels.firstOrNull()
-                item?.panelActivity
-            }
-            .stateIn(bgScope, SharingStarted.Eagerly, null)
-
-    private val taskFragmentFinished =
-        MutableSharedFlow<Long>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
-
-    fun onDreamEndUnexpectedly() {
-        powerManager.userActivity(
-            systemClock.uptimeMillis(),
-            PowerManager.USER_ACTIVITY_EVENT_OTHER,
-            PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
-        )
-        taskFragmentFinished.tryEmit(systemClock.currentTimeMillis())
-    }
-
-    /**
-     * Monitors if the current home panel package is updated and causes the dream to finish, and
-     * attempts to restart the dream in this case.
-     */
-    @SuppressLint("MissingPermission")
-    suspend fun monitorUpdatesAndRestart() {
-        taskFragmentFinished.resetReplayCache()
-        panelComponent
-            .flatMapLatest { component ->
-                if (component == null) return@flatMapLatest emptyFlow()
-                packageChangeInteractor.packageChanged(UserHandle.CURRENT, component.packageName)
-            }
-            .filter { it.isUpdate() }
-            // Wait for an UpdatedStarted - UpdateFinished pair to ensure the update has finished.
-            .pairwiseBy(::validateUpdatePair)
-            .filterNotNull()
-            .sample(taskFragmentFinished, ::Pair)
-            .filter { (updateStarted, lastFinishedTimestamp) ->
-                abs(updateStarted.timeMillis - lastFinishedTimestamp) <=
-                    MAX_UPDATE_CORRELATION_DELAY.inWholeMilliseconds
-            }
-            .collect { dreamManager.startDream() }
-    }
-
-    private data class PanelComponent(
-        val componentName: ComponentName,
-        val panelActivity: ComponentName,
-    )
-
-    companion object {
-        /**
-         * The maximum delay between a package update **starting** and the task fragment finishing
-         * which causes us to correlate the package update as the cause of the task fragment
-         * finishing.
-         */
-        val MAX_UPDATE_CORRELATION_DELAY = 500.milliseconds
-    }
-}
-
-private fun PackageChangeModel.isUpdate() =
-    this is PackageChangeModel.UpdateStarted || this is PackageChangeModel.UpdateFinished
-
-private fun validateUpdatePair(
-    updateStarted: PackageChangeModel,
-    updateFinished: PackageChangeModel
-): PackageChangeModel.UpdateStarted? =
-    when {
-        !updateStarted.isSamePackage(updateFinished) -> null
-        updateStarted !is PackageChangeModel.UpdateStarted -> null
-        updateFinished !is PackageChangeModel.UpdateFinished -> null
-        else -> updateStarted
-    }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt
new file mode 100644
index 0000000..2bcfea8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxy.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.homecontrols.service
+
+import android.content.ComponentName
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dreams.homecontrols.shared.IHomeControlsRemoteProxy
+import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.stateIn
+
+/** Class to wrap [IHomeControlsRemoteProxy], which exposes the current user's home controls info */
+class HomeControlsRemoteProxy
+@AssistedInject
+constructor(
+    @Background bgScope: CoroutineScope,
+    dumpManager: DumpManager,
+    @Assisted private val proxy: IHomeControlsRemoteProxy,
+) : FlowDumperImpl(dumpManager) {
+
+    private companion object {
+        const val TAG = "HomeControlsRemoteProxy"
+    }
+
+    val componentInfo: Flow<HomeControlsComponentInfo> =
+        conflatedCallbackFlow {
+                val listener =
+                    object : IOnControlsSettingsChangeListener.Stub() {
+                        override fun onControlsSettingsChanged(
+                            panelComponent: ComponentName?,
+                            allowTrivialControlsOnLockscreen: Boolean,
+                        ) {
+                            trySendWithFailureLogging(
+                                HomeControlsComponentInfo(
+                                    panelComponent,
+                                    allowTrivialControlsOnLockscreen,
+                                ),
+                                TAG,
+                            )
+                        }
+                    }
+                proxy.registerListenerForCurrentUser(listener)
+                awaitClose { proxy.unregisterListenerForCurrentUser(listener) }
+            }
+            .distinctUntilChanged()
+            .stateIn(bgScope, SharingStarted.WhileSubscribed(), null)
+            .dumpValue("componentInfo")
+            .filterNotNull()
+
+    @AssistedFactory
+    interface Factory {
+        fun create(proxy: IHomeControlsRemoteProxy): HomeControlsRemoteProxy
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt
new file mode 100644
index 0000000..b14903d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegator.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.homecontrols.service
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dreams.DreamLogger
+import com.android.systemui.dreams.homecontrols.dagger.HomeControlsRemoteServiceComponent
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsDataSource
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.dagger.DreamLog
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import com.android.systemui.util.service.ObservableServiceConnection
+import com.android.systemui.util.service.PersistentConnectionManager
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.dropWhile
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+
+/**
+ * Queries a remote service for [HomeControlsComponentInfo] necessary to show the home controls
+ * dream.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class RemoteHomeControlsDataSourceDelegator
+@Inject
+constructor(
+    @Background bgScope: CoroutineScope,
+    serviceFactory: HomeControlsRemoteServiceComponent.Factory,
+    @DreamLog logBuffer: LogBuffer,
+    dumpManager: DumpManager,
+) : HomeControlsDataSource, FlowDumperImpl(dumpManager) {
+    private val logger = DreamLogger(logBuffer, TAG)
+
+    private val connectionManager: PersistentConnectionManager<HomeControlsRemoteProxy> by lazy {
+        serviceFactory.create(callback).connectionManager
+    }
+
+    private val proxyState =
+        MutableStateFlow<HomeControlsRemoteProxy?>(null)
+            .apply {
+                subscriptionCount
+                    .map { it > 0 }
+                    .dropWhile { !it }
+                    .distinctUntilChanged()
+                    .onEach { active ->
+                        logger.d({ "Remote service connection active: $bool1" }) { bool1 = active }
+                        if (active) {
+                            connectionManager.start()
+                        } else {
+                            connectionManager.stop()
+                        }
+                    }
+                    .launchIn(bgScope)
+            }
+            .dumpValue("proxyState")
+
+    private val callback: ObservableServiceConnection.Callback<HomeControlsRemoteProxy> =
+        object : ObservableServiceConnection.Callback<HomeControlsRemoteProxy> {
+            override fun onConnected(
+                connection: ObservableServiceConnection<HomeControlsRemoteProxy>?,
+                proxy: HomeControlsRemoteProxy,
+            ) {
+                logger.d("Service connected")
+                proxyState.value = proxy
+            }
+
+            override fun onDisconnected(
+                connection: ObservableServiceConnection<HomeControlsRemoteProxy>?,
+                reason: Int,
+            ) {
+                logger.d({ "Service disconnected with reason $int1" }) { int1 = reason }
+                proxyState.value = null
+            }
+        }
+
+    override val componentInfo: Flow<HomeControlsComponentInfo> =
+        proxyState
+            .filterNotNull()
+            .flatMapLatest { proxy: HomeControlsRemoteProxy -> proxy.componentInfo }
+            .dumpWhileCollecting("componentInfo")
+
+    private companion object {
+        const val TAG = "HomeControlsRemoteDataSourceDelegator"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/TaskFragmentComponent.kt
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt
rename to packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/TaskFragmentComponent.kt
index d547de2..67de30c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/service/TaskFragmentComponent.kt
@@ -14,29 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dreams.homecontrols
+package com.android.systemui.dreams.homecontrols.service
 
 import android.app.Activity
-import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration
 import android.content.Intent
 import android.graphics.Rect
 import android.os.Binder
 import android.window.TaskFragmentCreationParams
 import android.window.TaskFragmentInfo
 import android.window.TaskFragmentOperation
-import android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT
-import android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK
 import android.window.TaskFragmentOrganizer
-import android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE
-import android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CLOSE
-import android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_OPEN
 import android.window.TaskFragmentTransaction
-import android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK
-import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED
-import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR
-import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED
-import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED
-import android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED
 import android.window.WindowContainerTransaction
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -65,7 +54,7 @@
             activity: Activity,
             @Assisted("onCreateCallback") onCreateCallback: FragmentInfoCallback,
             @Assisted("onInfoChangedCallback") onInfoChangedCallback: FragmentInfoCallback,
-            hide: () -> Unit
+            hide: () -> Unit,
         ): TaskFragmentComponent
     }
 
@@ -90,26 +79,28 @@
             change.taskFragmentInfo?.let { taskFragmentInfo ->
                 if (taskFragmentInfo.fragmentToken == fragmentToken) {
                     when (change.type) {
-                        TYPE_TASK_FRAGMENT_APPEARED -> {
+                        TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED -> {
                             resultT.addTaskFragmentOperation(
                                 fragmentToken,
-                                TaskFragmentOperation.Builder(OP_TYPE_REORDER_TO_TOP_OF_TASK)
-                                    .build()
+                                TaskFragmentOperation.Builder(
+                                        TaskFragmentOperation.OP_TYPE_REORDER_TO_TOP_OF_TASK
+                                    )
+                                    .build(),
                             )
 
                             onCreateCallback(taskFragmentInfo)
                         }
-                        TYPE_TASK_FRAGMENT_INFO_CHANGED -> {
+                        TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED -> {
                             onInfoChangedCallback(taskFragmentInfo)
                         }
-                        TYPE_TASK_FRAGMENT_VANISHED -> {
+                        TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED -> {
                             hide()
                         }
-                        TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED -> {}
-                        TYPE_TASK_FRAGMENT_ERROR -> {
+                        TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED -> {}
+                        TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR -> {
                             hide()
                         }
-                        TYPE_ACTIVITY_REPARENTED_TO_TASK -> {}
+                        TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK -> {}
                         else ->
                             throw IllegalArgumentException(
                                 "Unknown TaskFragmentEvent=" + change.type
@@ -121,8 +112,8 @@
         organizer.onTransactionHandled(
             transaction.transactionToken,
             resultT,
-            TASK_FRAGMENT_TRANSIT_CHANGE,
-            false
+            TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+            false,
         )
     }
 
@@ -132,15 +123,15 @@
             TaskFragmentCreationParams.Builder(
                     organizer.organizerToken,
                     fragmentToken,
-                    activity.activityToken!!
+                    activity.activityToken!!,
                 )
                 .setInitialRelativeBounds(Rect())
-                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
                 .build()
         organizer.applyTransaction(
             WindowContainerTransaction().createTaskFragment(fragmentOptions),
-            TASK_FRAGMENT_TRANSIT_CHANGE,
-            false
+            TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE,
+            false,
         )
     }
 
@@ -151,8 +142,8 @@
     fun startActivityInTaskFragment(intent: Intent) {
         organizer.applyTransaction(
             WindowContainerTransaction().startActivity(intent),
-            TASK_FRAGMENT_TRANSIT_OPEN,
-            false
+            TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_OPEN,
+            false,
         )
     }
 
@@ -162,10 +153,13 @@
             WindowContainerTransaction()
                 .addTaskFragmentOperation(
                     fragmentToken,
-                    TaskFragmentOperation.Builder(OP_TYPE_DELETE_TASK_FRAGMENT).build()
+                    TaskFragmentOperation.Builder(
+                            TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT
+                        )
+                        .build(),
                 ),
-            TASK_FRAGMENT_TRANSIT_CLOSE,
-            false
+            TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CLOSE,
+            false,
         )
         organizer.unregisterOrganizer()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IHomeControlsRemoteProxy.aidl b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IHomeControlsRemoteProxy.aidl
new file mode 100644
index 0000000..115b62c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IHomeControlsRemoteProxy.aidl
@@ -0,0 +1,9 @@
+package com.android.systemui.dreams.homecontrols.shared;
+
+import android.os.IRemoteCallback;
+import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener;
+
+oneway interface IHomeControlsRemoteProxy {
+    void registerListenerForCurrentUser(in IOnControlsSettingsChangeListener callback);
+    void unregisterListenerForCurrentUser(in IOnControlsSettingsChangeListener callback);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IOnControlsSettingsChangeListener.aidl b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IOnControlsSettingsChangeListener.aidl
new file mode 100644
index 0000000..99e5fae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/IOnControlsSettingsChangeListener.aidl
@@ -0,0 +1,7 @@
+package com.android.systemui.dreams.homecontrols.shared;
+
+import android.content.ComponentName;
+
+oneway interface IOnControlsSettingsChangeListener {
+    void onControlsSettingsChanged(in ComponentName panelComponent, boolean allowTrivialControlsOnLockscreen);
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsComponentInfo.kt
similarity index 72%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsComponentInfo.kt
index 83cbc52..b9e5080 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsComponentInfo.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.systemui.dreams.homecontrols.shared.model
+
+import android.content.ComponentName
+
+data class HomeControlsComponentInfo(
+    val componentName: ComponentName?,
+    val allowTrivialControlsOnLockscreen: Boolean,
+)
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSource.kt
similarity index 68%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSource.kt
index 83cbc52..8187c54 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSource.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.systemui.dreams.homecontrols.shared.model
+
+import kotlinx.coroutines.flow.Flow
+
+/** Source of data for home controls dream to get the necessary information it needs. */
+interface HomeControlsDataSource {
+    val componentInfo: Flow<HomeControlsComponentInfo>
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsDreamStartable.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartable.kt
rename to packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsDreamStartable.kt
index 03f58ac..644d5fd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsDreamStartable.kt
@@ -14,24 +14,28 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dreams.homecontrols
+package com.android.systemui.dreams.homecontrols.system
 
 import android.content.ComponentName
 import android.content.Context
 import android.content.pm.PackageManager
 import android.service.controls.flags.Flags.homePanelDream
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.homeControlsDreamHsum
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
+import com.android.systemui.dreams.homecontrols.HomeControlsDreamService
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.HomeControlsComponentInteractor
+import com.android.systemui.settings.UserContextProvider
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 class HomeControlsDreamStartable
 @Inject
 constructor(
     context: Context,
-    private val packageManager: PackageManager,
+    private val systemPackageManager: PackageManager,
+    private val userContextProvider: UserContextProvider,
     private val homeControlsComponentInteractor: HomeControlsComponentInteractor,
     @Background private val bgScope: CoroutineScope,
 ) : CoreStartable {
@@ -57,10 +61,16 @@
             } else {
                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED
             }
+        val packageManager =
+            if (homeControlsDreamHsum()) {
+                userContextProvider.userContext.packageManager
+            } else {
+                systemPackageManager
+            }
         packageManager.setComponentEnabledSetting(
             componentName,
             packageState,
-            PackageManager.DONT_KILL_APP
+            PackageManager.DONT_KILL_APP,
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteService.kt
new file mode 100644
index 0000000..a65d216
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/HomeControlsRemoteService.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.dreams.homecontrols.system
+
+import android.content.ComponentName
+import android.content.Intent
+import android.os.IBinder
+import android.os.RemoteCallbackList
+import android.os.RemoteException
+import android.util.Log
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleService
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.controls.settings.ControlsSettingsRepository
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dreams.DreamLogger
+import com.android.systemui.dreams.homecontrols.shared.IHomeControlsRemoteProxy
+import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.HomeControlsComponentInteractor
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.dagger.DreamLog
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.atomic.AtomicInteger
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.launch
+
+/**
+ * Service which exports the current home controls component name, for use in SystemUI processes
+ * running in other users. This service should only run in the system user.
+ */
+class HomeControlsRemoteService
+@Inject
+constructor(binderFactory: HomeControlsRemoteServiceBinder.Factory) : LifecycleService() {
+    val binder by lazy { binderFactory.create(this) }
+
+    override fun onBind(intent: Intent): IBinder? {
+        super.onBind(intent)
+        return binder
+    }
+}
+
+class HomeControlsRemoteServiceBinder
+@AssistedInject
+constructor(
+    private val homeControlsComponentInteractor: HomeControlsComponentInteractor,
+    private val controlsSettingsRepository: ControlsSettingsRepository,
+    @Background private val bgContext: CoroutineContext,
+    @DreamLog logBuffer: LogBuffer,
+    @Assisted lifecycleOwner: LifecycleOwner,
+) : IHomeControlsRemoteProxy.Stub(), LifecycleOwner by lifecycleOwner {
+    private val logger = DreamLogger(logBuffer, TAG)
+    private val callbacks =
+        object : RemoteCallbackList<IOnControlsSettingsChangeListener>() {
+            override fun onCallbackDied(listener: IOnControlsSettingsChangeListener?) {
+                if (callbackCount.decrementAndGet() == 0) {
+                    logger.d("Cancelling collection due to callback death")
+                    collectionJob?.cancel()
+                    collectionJob = null
+                }
+            }
+        }
+    private val callbackCount = AtomicInteger(0)
+    private var collectionJob: Job? = null
+
+    override fun registerListenerForCurrentUser(listener: IOnControlsSettingsChangeListener?) {
+        if (listener == null) return
+        logger.d("Register listener")
+        val registered = callbacks.register(listener)
+        if (registered && callbackCount.getAndIncrement() == 0) {
+            // If the first listener, start the collection job. This will also take
+            // care of notifying the listener of the initial state.
+            logger.d("Starting collection")
+            collectionJob =
+                lifecycleScope.launch(bgContext) {
+                    combine(
+                            homeControlsComponentInteractor.panelComponent,
+                            controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen,
+                        ) { panelComponent, allowTrivialControls ->
+                            callbacks.notifyAllCallbacks(panelComponent, allowTrivialControls)
+                        }
+                        .launchIn(this)
+                }
+        } else if (registered) {
+            // If not the first listener, notify the listener of the current value immediately.
+            listener.notify(
+                homeControlsComponentInteractor.panelComponent.value,
+                controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen.value,
+            )
+        }
+    }
+
+    override fun unregisterListenerForCurrentUser(listener: IOnControlsSettingsChangeListener?) {
+        if (listener == null) return
+        logger.d("Unregister listener")
+        if (callbacks.unregister(listener) && callbackCount.decrementAndGet() == 0) {
+            logger.d("Cancelling collection due to unregister")
+            collectionJob?.cancel()
+            collectionJob = null
+        }
+    }
+
+    private companion object {
+        const val TAG = "HomeControlsRemoteServiceBinder"
+    }
+
+    private fun IOnControlsSettingsChangeListener.notify(
+        panelComponent: ComponentName?,
+        allowTrivialControlsOnLockscreen: Boolean,
+    ) {
+        try {
+            onControlsSettingsChanged(panelComponent, allowTrivialControlsOnLockscreen)
+        } catch (e: RemoteException) {
+            Log.e(TAG, "Error notifying callback", e)
+        }
+    }
+
+    private fun RemoteCallbackList<IOnControlsSettingsChangeListener>.notifyAllCallbacks(
+        panelComponent: ComponentName?,
+        allowTrivialControlsOnLockscreen: Boolean,
+    ) {
+        val itemCount = beginBroadcast()
+        try {
+            for (i in 0 until itemCount) {
+                getBroadcastItem(i).notify(panelComponent, allowTrivialControlsOnLockscreen)
+            }
+        } finally {
+            finishBroadcast()
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(lifecycleOwner: LifecycleOwner): HomeControlsRemoteServiceBinder
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/LocalHomeControlsDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/LocalHomeControlsDataSourceDelegator.kt
new file mode 100644
index 0000000..ca255fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/LocalHomeControlsDataSourceDelegator.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.dreams.homecontrols.system
+
+import com.android.systemui.controls.settings.ControlsSettingsRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsComponentInfo
+import com.android.systemui.dreams.homecontrols.shared.model.HomeControlsDataSource
+import com.android.systemui.dreams.homecontrols.system.domain.interactor.HomeControlsComponentInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+/**
+ * Queries local data sources for the [HomeControlsComponentInfo] necessary to show the home
+ * controls dream.
+ */
+@SysUISingleton
+class LocalHomeControlsDataSourceDelegator
+@Inject
+constructor(
+    homeControlsComponentInteractor: HomeControlsComponentInteractor,
+    controlsSettingsRepository: ControlsSettingsRepository,
+) : HomeControlsDataSource {
+    override val componentInfo: Flow<HomeControlsComponentInfo> =
+        combine(
+            homeControlsComponentInteractor.panelComponent,
+            controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen,
+        ) { panelComponent, allowActionOnTrivialControlsInLockscreen ->
+            HomeControlsComponentInfo(panelComponent, allowActionOnTrivialControlsInLockscreen)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractor.kt
new file mode 100644
index 0000000..31bd708
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractor.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.systemui.dreams.homecontrols.system.domain.interactor
+
+import android.content.ComponentName
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.dagger.ControlsComponent
+import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.panels.AuthorizedPanelsRepository
+import com.android.systemui.controls.panels.SelectedComponentRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.kotlin.getOrNull
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+@OptIn(ExperimentalCoroutinesApi::class)
+class HomeControlsComponentInteractor
+@Inject
+constructor(
+    private val selectedComponentRepository: SelectedComponentRepository,
+    controlsComponent: ControlsComponent,
+    authorizedPanelsRepository: AuthorizedPanelsRepository,
+    userRepository: UserRepository,
+    @Background private val bgScope: CoroutineScope,
+) {
+    private val controlsListingController: ControlsListingController? =
+        controlsComponent.getControlsListingController().getOrNull()
+
+    /** Gets the current user's selected panel, or null if there isn't one */
+    private val selectedPanel: Flow<SelectedComponentRepository.SelectedComponent?> =
+        userRepository.selectedUserInfo
+            .flatMapLatest { user ->
+                selectedComponentRepository.selectedComponentFlow(user.userHandle)
+            }
+            .map { if (it?.isPanel == true) it else null }
+
+    /** Gets the current user's authorized panels */
+    private val allAuthorizedPanels: Flow<Set<String>> =
+        userRepository.selectedUserInfo.flatMapLatest { user ->
+            authorizedPanelsRepository.observeAuthorizedPanels(user.userHandle)
+        }
+
+    /** Gets all the available services from [ControlsListingController] */
+    private fun allAvailableServices(): Flow<List<ControlsServiceInfo>> {
+        if (controlsListingController == null) {
+            return emptyFlow()
+        }
+        return conflatedCallbackFlow {
+                val listener =
+                    object : ControlsListingController.ControlsListingCallback {
+                        override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
+                            trySend(serviceInfos)
+                        }
+                    }
+                controlsListingController.addCallback(listener)
+                awaitClose { controlsListingController.removeCallback(listener) }
+            }
+            .onStart { emit(controlsListingController.getCurrentServices()) }
+    }
+
+    /** Gets all panels which are available and authorized by the user */
+    private val allAvailableAndAuthorizedPanels: Flow<List<PanelComponent>> =
+        combine(allAvailableServices(), allAuthorizedPanels) { serviceInfos, authorizedPanels ->
+            serviceInfos.mapNotNull {
+                val panelActivity = it.panelActivity
+                if (it.componentName.packageName in authorizedPanels && panelActivity != null) {
+                    PanelComponent(it.componentName, panelActivity)
+                } else {
+                    null
+                }
+            }
+        }
+
+    val panelComponent: StateFlow<ComponentName?> =
+        combine(allAvailableAndAuthorizedPanels, selectedPanel) { panels, selected ->
+                val item =
+                    panels.firstOrNull { it.componentName == selected?.componentName }
+                        ?: panels.firstOrNull()
+                item?.panelActivity
+            }
+            .stateIn(bgScope, SharingStarted.Eagerly, null)
+
+    private data class PanelComponent(
+        val componentName: ComponentName,
+        val panelActivity: ComponentName,
+    )
+}
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/msdl/qs/TileHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
index 7905950..ed7d182 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/qs/TileHapticsViewModel.kt
@@ -38,7 +38,7 @@
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.transform
 
-/** A view-model to trigger haptics feedback on Quick Settings tiles */
+/** A view-model to trigger haptic feedback on Quick Settings tiles */
 @OptIn(ExperimentalCoroutinesApi::class)
 class TileHapticsViewModel
 @AssistedInject
@@ -149,7 +149,7 @@
             onActivityLaunchTransitionEnd = ::onActivityLaunchTransitionEnd,
         )
 
-    /** Models the state of toggle haptics to play */
+    /** Models the state of haptics to play */
     enum class TileHapticsState {
         TOGGLE_ON,
         TOGGLE_OFF,
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..1b044de 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,74 @@
                 .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 = onDoneButtonClicked,
+            modifier = Modifier.graphicsLayer { alpha = buttonAlpha },
+            enabled = actionState is Finished,
+        )
+    }
+}
+
+@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/inputdevice/tutorial/ui/composable/TutorialComponents.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialComponents.kt
index 01ad585..202dba3 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialComponents.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialComponents.kt
@@ -28,13 +28,17 @@
 import com.android.systemui.res.R
 
 @Composable
-fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) {
+fun DoneButton(
+    onDoneButtonClicked: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+) {
     Row(
         horizontalArrangement = Arrangement.End,
         verticalAlignment = Alignment.CenterVertically,
-        modifier = modifier.fillMaxWidth()
+        modifier = modifier.fillMaxWidth(),
     ) {
-        Button(onClick = onDoneButtonClicked) {
+        Button(onClick = onDoneButtonClicked, enabled = enabled) {
             Text(stringResource(R.string.touchpad_tutorial_done_button))
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
index a085887..12dd581 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
@@ -19,12 +19,14 @@
 import android.content.Context
 import android.graphics.drawable.Icon
 import android.hardware.input.InputManager
+import android.hardware.input.KeyGlyphMap
 import android.util.Log
 import android.view.InputDevice
 import android.view.KeyCharacterMap
 import android.view.KeyEvent
 import android.view.KeyboardShortcutGroup
 import android.view.KeyboardShortcutInfo
+import com.android.systemui.Flags.shortcutHelperKeyGlyph
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.shortcut.data.source.KeyboardShortcutGroupsSource
@@ -142,7 +144,10 @@
         return if (type == null) {
             null
         } else {
+            val keyGlyphMap =
+                if (shortcutHelperKeyGlyph()) inputManager.getKeyGlyphMap(inputDevice.id) else null
             toShortcutCategory(
+                keyGlyphMap,
                 inputDevice.keyCharacterMap,
                 type,
                 groups,
@@ -163,6 +168,7 @@
     }
 
     private fun toShortcutCategory(
+        keyGlyphMap: KeyGlyphMap?,
         keyCharacterMap: KeyCharacterMap,
         type: ShortcutCategoryType,
         shortcutGroups: List<KeyboardShortcutGroup>,
@@ -175,6 +181,7 @@
                     ShortcutSubCategory(
                         shortcutGroup.label.toString(),
                         toShortcuts(
+                            keyGlyphMap,
                             keyCharacterMap,
                             shortcutGroup.items,
                             keepIcons,
@@ -192,6 +199,7 @@
     }
 
     private fun toShortcuts(
+        keyGlyphMap: KeyGlyphMap?,
         keyCharacterMap: KeyCharacterMap,
         infoList: List<KeyboardShortcutInfo>,
         keepIcons: Boolean,
@@ -203,14 +211,16 @@
                 // keycode, or they could have a baseCharacter instead of a keycode.
                 it.keycode == KeyEvent.KEYCODE_UNKNOWN || supportedKeyCodes.contains(it.keycode)
             }
-            .mapNotNull { toShortcut(keyCharacterMap, it, keepIcons) }
+            .mapNotNull { toShortcut(keyGlyphMap, keyCharacterMap, it, keepIcons) }
 
     private fun toShortcut(
+        keyGlyphMap: KeyGlyphMap?,
         keyCharacterMap: KeyCharacterMap,
         shortcutInfo: KeyboardShortcutInfo,
         keepIcon: Boolean,
     ): Shortcut? {
-        val shortcutCommand = toShortcutCommand(keyCharacterMap, shortcutInfo) ?: return null
+        val shortcutCommand =
+            toShortcutCommand(keyGlyphMap, keyCharacterMap, shortcutInfo) ?: return null
         return Shortcut(
             label = shortcutInfo.label!!.toString(),
             icon = toShortcutIcon(keepIcon, shortcutInfo),
@@ -235,6 +245,7 @@
     }
 
     private fun toShortcutCommand(
+        keyGlyphMap: KeyGlyphMap?,
         keyCharacterMap: KeyCharacterMap,
         info: KeyboardShortcutInfo,
     ): ShortcutCommand? {
@@ -242,7 +253,7 @@
         var remainingModifiers = info.modifiers
         SUPPORTED_MODIFIERS.forEach { supportedModifier ->
             if ((supportedModifier and remainingModifiers) != 0) {
-                keys += toShortcutModifierKey(supportedModifier) ?: return null
+                keys += toShortcutModifierKey(keyGlyphMap, supportedModifier) ?: return null
                 // "Remove" the modifier from the remaining modifiers
                 remainingModifiers = remainingModifiers and supportedModifier.inv()
             }
@@ -253,7 +264,9 @@
             return null
         }
         if (info.keycode != 0 || info.baseCharacter > Char.MIN_VALUE) {
-            keys += toShortcutKey(keyCharacterMap, info.keycode, info.baseCharacter) ?: return null
+            keys +=
+                toShortcutKey(keyGlyphMap, keyCharacterMap, info.keycode, info.baseCharacter)
+                    ?: return null
         }
         if (keys.isEmpty()) {
             Log.wtf(TAG, "No keys for $info")
@@ -262,10 +275,15 @@
         return ShortcutCommand(keys)
     }
 
-    private fun toShortcutModifierKey(modifierMask: Int): ShortcutKey? {
+    private fun toShortcutModifierKey(keyGlyphMap: KeyGlyphMap?, modifierMask: Int): ShortcutKey? {
+        val modifierDrawable = keyGlyphMap?.getDrawableForModifierState(context, modifierMask)
+        if (modifierDrawable != null) {
+            return ShortcutKey.Icon.DrawableIcon(drawable = modifierDrawable)
+        }
+
         val iconResId = ShortcutHelperKeys.keyIcons[modifierMask]
         if (iconResId != null) {
-            return ShortcutKey.Icon(iconResId)
+            return ShortcutKey.Icon.ResIdIcon(iconResId)
         }
 
         val modifierLabel = ShortcutHelperKeys.modifierLabels[modifierMask]
@@ -277,13 +295,19 @@
     }
 
     private fun toShortcutKey(
+        keyGlyphMap: KeyGlyphMap?,
         keyCharacterMap: KeyCharacterMap,
         keyCode: Int,
         baseCharacter: Char = Char.MIN_VALUE,
     ): ShortcutKey? {
+        val keycodeDrawable = keyGlyphMap?.getDrawableForKeycode(context, keyCode)
+        if (keycodeDrawable != null) {
+            return ShortcutKey.Icon.DrawableIcon(drawable = keycodeDrawable)
+        }
+
         val iconResId = ShortcutHelperKeys.keyIcons[keyCode]
         if (iconResId != null) {
-            return ShortcutKey.Icon(iconResId)
+            return ShortcutKey.Icon.ResIdIcon(iconResId)
         }
         if (baseCharacter > Char.MIN_VALUE) {
             return ShortcutKey.Text(baseCharacter.uppercase())
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
index eddac4d..05ff0cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
@@ -70,7 +70,7 @@
             },
             //  Change split screen focus to LHS:
             //   - Meta + Alt + Left arrow
-            shortcutInfo(resources.getString(R.string.system_multitasking_splitscreen_focus_rhs)) {
+            shortcutInfo(resources.getString(R.string.system_multitasking_splitscreen_focus_lhs)) {
                 command(META_META_ON or META_ALT_ON, KEYCODE_DPAD_LEFT)
             },
         )
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
index e5b8096..28451ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
@@ -28,7 +28,7 @@
     }
 
     fun key(@DrawableRes drawableResId: Int) {
-        keys += ShortcutKey.Icon(drawableResId)
+        keys += ShortcutKey.Icon.ResIdIcon(drawableResId)
     }
 
     fun build() = ShortcutCommand(keys)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt
index 1abb78c..1a609ea 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt
@@ -16,10 +16,15 @@
 
 package com.android.systemui.keyboard.shortcut.shared.model
 
+import android.graphics.drawable.Drawable
 import androidx.annotation.DrawableRes
 
 sealed interface ShortcutKey {
     data class Text(val value: String) : ShortcutKey
 
-    data class Icon(@DrawableRes val drawableResId: Int) : ShortcutKey
+    sealed interface Icon : ShortcutKey {
+        data class ResIdIcon(@DrawableRes val drawableResId: Int) : Icon
+
+        data class DrawableIcon(val drawable: Drawable) : Icon
+    }
 }
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..abddc70 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,
+            )
+        }
     }
 }
 
@@ -554,7 +647,11 @@
 @Composable
 private fun BoxScope.ShortcutIconKey(key: ShortcutKey.Icon) {
     Icon(
-        painter = painterResource(key.drawableResId),
+        painter =
+            when (key) {
+                is ShortcutKey.Icon.ResIdIcon -> painterResource(key.drawableResId)
+                is ShortcutKey.Icon.DrawableIcon -> rememberDrawablePainter(drawable = key.drawable)
+            },
         contentDescription = null,
         modifier = Modifier.align(Alignment.Center).padding(6.dp),
     )
@@ -700,12 +797,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 +856,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/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/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index d0a40ec..7638079 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -61,8 +61,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor;
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
-import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule;
 import com.android.systemui.log.SessionTracker;
@@ -239,12 +237,6 @@
 
     /** */
     @Provides
-    static KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
-        return new KeyguardQuickAffordancesMetricsLoggerImpl();
-    }
-
-    /** */
-    @Provides
     @SysUISingleton
     static ThreadAssert providesThreadAssert() {
         return new ThreadAssert();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index e68d799..4d999df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -106,7 +106,7 @@
                                     trySendWithFailureLogging(
                                         getFpSensorType(),
                                         TAG,
-                                        "onAllAuthenticatorsRegistered, emitting fpSensorType"
+                                        "onAllAuthenticatorsRegistered, emitting fpSensorType",
                                     )
                             }
                         }
@@ -114,7 +114,7 @@
                     trySendWithFailureLogging(
                         getFpSensorType(),
                         TAG,
-                        "initial value for fpSensorType"
+                        "initial value for fpSensorType",
                     )
                     awaitClose { authController.removeCallback(callback) }
                 }
@@ -134,7 +134,7 @@
                         trySendWithFailureLogging(
                             keyguardUpdateMonitor.isFingerprintLockedOut,
                             TAG,
-                            "onLockedOutStateChanged"
+                            "onLockedOutStateChanged",
                         )
                     }
                 val callback =
@@ -154,7 +154,7 @@
             .stateIn(
                 scope,
                 started = Eagerly,
-                initialValue = keyguardUpdateMonitor.isFingerprintLockedOut
+                initialValue = keyguardUpdateMonitor.isFingerprintLockedOut,
             )
     }
 
@@ -165,13 +165,13 @@
                         object : KeyguardUpdateMonitorCallback() {
                             override fun onBiometricRunningStateChanged(
                                 running: Boolean,
-                                biometricSourceType: BiometricSourceType?
+                                biometricSourceType: BiometricSourceType?,
                             ) {
                                 if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
                                     trySendWithFailureLogging(
                                         running,
                                         TAG,
-                                        "Fingerprint running state changed"
+                                        "Fingerprint running state changed",
                                     )
                                 }
                             }
@@ -180,7 +180,7 @@
                     trySendWithFailureLogging(
                         keyguardUpdateMonitor.isFingerprintDetectionRunning,
                         TAG,
-                        "Initial fingerprint running state"
+                        "Initial fingerprint running state",
                     )
                     awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
                 }
@@ -193,11 +193,7 @@
             .map { it.isEngaged }
             .filterNotNull()
             .map { it }
-            .stateIn(
-                scope = scope,
-                started = WhileSubscribed(),
-                initialValue = false,
-            )
+            .stateIn(scope = scope, started = WhileSubscribed(), initialValue = false)
 
     // TODO(b/322555228) Remove after consolidating device entry auth messages with BP auth messages
     //  in BiometricStatusRepository
@@ -232,10 +228,7 @@
                             ) {
                                 sendUpdateIfFingerprint(
                                     biometricSourceType,
-                                    ErrorFingerprintAuthenticationStatus(
-                                        msgId,
-                                        errString,
-                                    ),
+                                    ErrorFingerprintAuthenticationStatus(msgId, errString),
                                 )
                             }
 
@@ -246,15 +239,12 @@
                             ) {
                                 sendUpdateIfFingerprint(
                                     biometricSourceType,
-                                    HelpFingerprintAuthenticationStatus(
-                                        msgId,
-                                        helpString,
-                                    ),
+                                    HelpFingerprintAuthenticationStatus(msgId, helpString),
                                 )
                             }
 
                             override fun onBiometricAuthFailed(
-                                biometricSourceType: BiometricSourceType,
+                                biometricSourceType: BiometricSourceType
                             ) {
                                 sendUpdateIfFingerprint(
                                     biometricSourceType,
@@ -270,14 +260,14 @@
                                     biometricSourceType,
                                     AcquiredFingerprintAuthenticationStatus(
                                         AuthenticationReason.DeviceEntryAuthentication,
-                                        acquireInfo
+                                        acquireInfo,
                                     ),
                                 )
                             }
 
                             private fun sendUpdateIfFingerprint(
                                 biometricSourceType: BiometricSourceType,
-                                authenticationStatus: FingerprintAuthenticationStatus
+                                authenticationStatus: FingerprintAuthenticationStatus,
                             ) {
                                 if (biometricSourceType != BiometricSourceType.FINGERPRINT) {
                                     return
@@ -285,13 +275,14 @@
                                 trySendWithFailureLogging(
                                     authenticationStatus,
                                     TAG,
-                                    "new fingerprint authentication status"
+                                    "new fingerprint authentication status",
                                 )
                             }
                         }
                     keyguardUpdateMonitor.registerCallback(callback)
                     awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
                 }
+                .flowOn(mainDispatcher)
                 .buffer(capacity = 4)
 
     override val shouldUpdateIndicatorVisibility: Flow<Boolean> =
@@ -302,7 +293,7 @@
                             shouldUpdateIndicatorVisibility,
                             TAG,
                             "Error sending shouldUpdateIndicatorVisibility " +
-                                "$shouldUpdateIndicatorVisibility"
+                                "$shouldUpdateIndicatorVisibility",
                         )
                     }
 
@@ -310,7 +301,7 @@
                     object : KeyguardUpdateMonitorCallback() {
                         override fun onBiometricRunningStateChanged(
                             running: Boolean,
-                            biometricSourceType: BiometricSourceType?
+                            biometricSourceType: BiometricSourceType?,
                         ) {
                             sendShouldUpdateIndicatorVisibility(true)
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBypassRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBypassRepository.kt
new file mode 100644
index 0000000..be4ab4b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBypassRepository.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.annotation.IntDef
+import android.content.res.Resources
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.keyguard.shared.model.DevicePosture.UNKNOWN
+import com.android.systemui.res.R
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class KeyguardBypassRepository
+@Inject
+constructor(
+    @Main resources: Resources,
+    biometricSettingsRepository: BiometricSettingsRepository,
+    devicePostureRepository: DevicePostureRepository,
+    dumpManager: DumpManager,
+    private val tunerService: TunerService,
+    @Background backgroundDispatcher: CoroutineDispatcher,
+) : FlowDumperImpl(dumpManager) {
+
+    @get:BypassOverride
+    private val bypassOverride: Int by lazy {
+        resources.getInteger(R.integer.config_face_unlock_bypass_override)
+    }
+
+    private val configFaceAuthSupportedPosture: DevicePosture by lazy {
+        DevicePosture.toPosture(resources.getInteger(R.integer.config_face_auth_supported_posture))
+    }
+
+    private val dismissByDefault: Int by lazy {
+        if (resources.getBoolean(com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) {
+            1
+        } else {
+            0
+        }
+    }
+
+    private var bypassEnabledSetting: Flow<Boolean> =
+        callbackFlow {
+                val updateBypassSetting = { state: Boolean ->
+                    trySendWithFailureLogging(state, TAG, "Error sending bypassSetting $state")
+                }
+
+                val tunable =
+                    TunerService.Tunable { key, _ ->
+                        updateBypassSetting(tunerService.getValue(key, dismissByDefault) != 0)
+                    }
+
+                updateBypassSetting(false)
+                tunerService.addTunable(tunable, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
+                awaitClose { tunerService.removeTunable(tunable) }
+            }
+            .flowOn(backgroundDispatcher)
+            .dumpWhileCollecting("bypassEnabledSetting")
+
+    val overrideFaceBypassSetting: Flow<Boolean> =
+        when (bypassOverride) {
+            FACE_UNLOCK_BYPASS_ALWAYS -> flowOf(true)
+            FACE_UNLOCK_BYPASS_NEVER -> flowOf(false)
+            else -> bypassEnabledSetting
+        }
+
+    val isPostureAllowedForFaceAuth: Flow<Boolean> =
+        when (configFaceAuthSupportedPosture) {
+            UNKNOWN -> flowOf(true)
+            else ->
+                devicePostureRepository.currentDevicePosture
+                    .map { posture -> posture == configFaceAuthSupportedPosture }
+                    .distinctUntilChanged()
+        }
+
+    /**
+     * Whether bypass is available.
+     *
+     * Bypass is the ability to skip the lockscreen when the device is unlocked using non-primary
+     * authentication types like face unlock, instead of requiring the user to explicitly dismiss
+     * the lockscreen by swiping after the device is already unlocked.
+     *
+     * "Available" refers to a combination of the user setting to skip the lockscreen being set,
+     * whether hard-wired OEM-overridable configs allow the feature, whether a foldable is in the
+     * right foldable posture, and other such things. It does _not_ model this based on more
+     * runtime-like states of the UI.
+     */
+    val isBypassAvailable: Flow<Boolean> =
+        combine(
+                overrideFaceBypassSetting,
+                biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
+                isPostureAllowedForFaceAuth,
+            ) {
+                bypassOverride: Boolean,
+                isFaceEnrolledAndEnabled: Boolean,
+                isPostureAllowedForFaceAuth: Boolean ->
+                bypassOverride && isFaceEnrolledAndEnabled && isPostureAllowedForFaceAuth
+            }
+            .distinctUntilChanged()
+            .dumpWhileCollecting("isBypassAvailable")
+
+    @IntDef(FACE_UNLOCK_BYPASS_NO_OVERRIDE, FACE_UNLOCK_BYPASS_ALWAYS, FACE_UNLOCK_BYPASS_NEVER)
+    @Retention(AnnotationRetention.SOURCE)
+    private annotation class BypassOverride
+
+    companion object {
+        private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
+        private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
+        private const val FACE_UNLOCK_BYPASS_NEVER = 2
+
+        private const val TAG = "KeyguardBypassRepository"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index d49550e..d0de21b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -36,12 +36,14 @@
 import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.kotlin.FlowDumperImpl
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -64,7 +66,14 @@
     configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>,
     dumpManager: DumpManager,
     userHandle: UserHandle,
-) {
+) : FlowDumperImpl(dumpManager) {
+    /**
+     * Whether a quick affordance is being launched. Quick Affordances are interactive lockscreen UI
+     * elements that allow the user to perform quick actions without unlocking their device.
+     */
+    val launchingAffordance: MutableStateFlow<Boolean> =
+        MutableStateFlow(false).dumpValue("launchingAffordance")
+
     // Configs for all keyguard quick affordances, mapped by the quick affordance ID as key
     private val configsByAffordanceId: Map<String, KeyguardQuickAffordanceConfig> =
         configs.associateBy { it.key }
@@ -112,11 +121,7 @@
                     }
                 }
             }
-            .stateIn(
-                scope = scope,
-                started = SharingStarted.Eagerly,
-                initialValue = emptyMap(),
-            )
+            .stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = emptyMap())
 
     init {
         legacySettingSyncer.startSyncing()
@@ -144,14 +149,8 @@
      * Updates the IDs of affordances to show at the slot with the given ID. The order of affordance
      * IDs should be descending priority order.
      */
-    fun setSelections(
-        slotId: String,
-        affordanceIds: List<String>,
-    ) {
-        selectionManager.value.setSelections(
-            slotId = slotId,
-            affordanceIds = affordanceIds,
-        )
+    fun setSelections(slotId: String, affordanceIds: List<String>) {
+        selectionManager.value.setSelections(slotId = slotId, affordanceIds = affordanceIds)
     }
 
     /**
@@ -222,10 +221,7 @@
             val (slotId, slotCapacity) = parseSlot(unparsedSlot)
             check(!seenSlotIds.contains(slotId)) { "Duplicate slot \"$slotId\"!" }
             seenSlotIds.add(slotId)
-            KeyguardSlotPickerRepresentation(
-                id = slotId,
-                maxSelectedAffordances = slotCapacity,
-            )
+            KeyguardSlotPickerRepresentation(id = slotId, maxSelectedAffordances = slotCapacity)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/PulseExpansionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/PulseExpansionRepository.kt
new file mode 100644
index 0000000..7699bab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/PulseExpansionRepository.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.keyguard.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@SysUISingleton
+class PulseExpansionRepository @Inject constructor(dumpManager: DumpManager) :
+    FlowDumperImpl(dumpManager) {
+    /**
+     * Whether the notification panel is expanding from the user swiping downward on a notification
+     * from the pulsing state, or swiping anywhere on the screen when face bypass is enabled
+     */
+    val isPulseExpanding: MutableStateFlow<Boolean> =
+        MutableStateFlow(false).dumpValue("pulseExpanding")
+}
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 b60e98a..8c60371 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
@@ -19,7 +19,6 @@
 import android.animation.ValueAnimator
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Flags.communalSceneKtfRefactor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -39,19 +38,19 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 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
-import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+import java.util.UUID
+import javax.inject.Inject
 import com.android.app.tracing.coroutines.launchTraced as launch
 
 @SysUISingleton
@@ -176,98 +175,101 @@
         if (SceneContainerFlag.isEnabled) return
         var transitionId: UUID? = null
         scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
-            shadeRepository.legacyShadeExpansion
-                .sampleCombine(
-                    transitionInteractor.startedKeyguardTransitionStep,
-                    keyguardInteractor.statusBarState,
-                    keyguardInteractor.isKeyguardDismissible,
-                    keyguardInteractor.isKeyguardOccluded,
-                )
-                .collect {
-                    (
-                        shadeExpansion,
-                        startedStep,
-                        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
-                            // `updateTransition` will control it until FINISHED or CANCELED
-                            var nextState =
-                                if (shadeExpansion == 0f) {
-                                    TransitionState.FINISHED
-                                } else if (shadeExpansion == 1f) {
-                                    TransitionState.CANCELED
-                                } else {
-                                    TransitionState.RUNNING
-                                }
+            shadeRepository.legacyShadeExpansion.collect { shadeExpansion ->
+                val statusBarState = keyguardInteractor.statusBarState.value
+                val isKeyguardUnlocked = keyguardInteractor.isKeyguardDismissible.value
+                val isKeyguardOccluded = keyguardInteractor.isKeyguardOccluded.value
+                val startedStep = transitionInteractor.startedKeyguardTransitionStep.value
 
-                            // startTransition below will issue the CANCELED directly
-                            if (nextState != TransitionState.CANCELED) {
-                                transitionRepository.updateTransition(
-                                    id,
-                                    // This maps the shadeExpansion to a much faster curve, to match
-                                    // the existing logic
-                                    1f -
-                                        MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, shadeExpansion),
-                                    nextState,
-                                )
+                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
+                        // `updateTransition` will control it until FINISHED or CANCELED
+                        var nextState =
+                            if (shadeExpansion == 0f) {
+                                TransitionState.FINISHED
+                            } else if (shadeExpansion == 1f) {
+                                TransitionState.CANCELED
+                            } else {
+                                TransitionState.RUNNING
                             }
 
-                            if (
-                                nextState == TransitionState.CANCELED ||
-                                    nextState == TransitionState.FINISHED
-                            ) {
-                                transitionId = null
-                            }
-
-                            // If canceled, just put the state back
-                            // TODO(b/278086361): This logic should happen in
-                            //  FromPrimaryBouncerInteractor.
-                            if (nextState == TransitionState.CANCELED) {
-                                transitionRepository.startTransition(
-                                    TransitionInfo(
-                                        ownerName =
-                                            "$name " +
-                                                "(on behalf of FromPrimaryBouncerInteractor)",
-                                        from = KeyguardState.PRIMARY_BOUNCER,
-                                        to =
-                                            if (isKeyguardOccluded) KeyguardState.OCCLUDED
-                                            else KeyguardState.LOCKSCREEN,
-                                        modeOnCanceled = TransitionModeOnCanceled.REVERSE,
-                                        animator =
-                                            getDefaultAnimatorForTransitionsToState(
-                                                    KeyguardState.LOCKSCREEN
-                                                )
-                                                .apply { duration = 100L },
-                                    )
-                                )
-                            }
+                        // startTransition below will issue the CANCELED directly
+                        if (nextState != TransitionState.CANCELED) {
+                            transitionRepository.updateTransition(
+                                id,
+                                // This maps the shadeExpansion to a much faster curve, to match
+                                // the existing logic
+                                1f - MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, shadeExpansion),
+                                nextState,
+                            )
                         }
-                    } else {
-                        // TODO (b/251849525): Remove statusbarstate check when that state is
-                        // integrated into KeyguardTransitionRepository
+
                         if (
-                            // Use currentTransitionInfo to decide whether to start the transition.
-                            currentTransitionInfo.to == KeyguardState.LOCKSCREEN &&
-                                shadeExpansion > 0f &&
-                                shadeExpansion < 1f &&
-                                shadeRepository.legacyShadeTracking.value &&
-                                !isKeyguardUnlocked &&
-                                statusBarState == KEYGUARD
+                            nextState == TransitionState.CANCELED ||
+                                nextState == TransitionState.FINISHED
                         ) {
-                            transitionId =
-                                startTransitionTo(
-                                    toState = KeyguardState.PRIMARY_BOUNCER,
-                                    animator = null, // transition will be manually controlled,
-                                    ownerReason = "#listenForLockscreenToPrimaryBouncerDragging",
+                            transitionId = null
+                        }
+
+                        // If canceled, just put the state back
+                        // TODO(b/278086361): This logic should happen in
+                        //  FromPrimaryBouncerInteractor.
+                        if (nextState == TransitionState.CANCELED) {
+                            transitionRepository.startTransition(
+                                TransitionInfo(
+                                    ownerName =
+                                        "$name " + "(on behalf of FromPrimaryBouncerInteractor)",
+                                    from = KeyguardState.PRIMARY_BOUNCER,
+                                    to =
+                                        if (isKeyguardOccluded) KeyguardState.OCCLUDED
+                                        else KeyguardState.LOCKSCREEN,
+                                    modeOnCanceled = TransitionModeOnCanceled.REVERSE,
+                                    animator =
+                                        getDefaultAnimatorForTransitionsToState(
+                                                KeyguardState.LOCKSCREEN
+                                            )
+                                            .apply { duration = 100L },
                                 )
+                            )
                         }
                     }
+                } else {
+                    // TODO (b/251849525): Remove statusbarstate check when that state is
+                    // integrated into KeyguardTransitionRepository
+                    if (
+                        // Use currentTransitionInfo to decide whether to start the transition.
+                        currentTransitionInfo.to == KeyguardState.LOCKSCREEN &&
+                            shadeExpansion > 0f &&
+                            shadeExpansion < 1f &&
+                            shadeRepository.legacyShadeTracking.value &&
+                            !isKeyguardUnlocked &&
+                            statusBarState == KEYGUARD
+                    ) {
+                        transitionId =
+                            startTransitionTo(
+                                toState = KeyguardState.PRIMARY_BOUNCER,
+                                animator = null, // transition will be manually controlled,
+                                ownerReason = "#listenForLockscreenToPrimaryBouncerDragging",
+                            )
+                    }
+                }
+            }
+        }
+
+        // Ensure that transitionId is nulled out if external signals cause a PRIMARY_BOUNCER
+        // transition to be canceled.
+        scope.launch {
+            transitionInteractor.transitions
+                .filter {
+                    it.transitionState == TransitionState.CANCELED &&
+                        it.to == KeyguardState.PRIMARY_BOUNCER
+                }
+                .collect {
+                    transitionId = null
                 }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractor.kt
new file mode 100644
index 0000000..d793064
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractor.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.data.repository.KeyguardBypassRepository
+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.util.kotlin.FlowDumperImpl
+import com.android.systemui.util.kotlin.combine
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class KeyguardBypassInteractor
+@Inject
+constructor(
+    keyguardBypassRepository: KeyguardBypassRepository,
+    alternateBouncerInteractor: AlternateBouncerInteractor,
+    keyguardQuickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
+    pulseExpansionInteractor: PulseExpansionInteractor,
+    sceneInteractor: SceneInteractor,
+    shadeInteractor: ShadeInteractor,
+    dumpManager: DumpManager,
+) : FlowDumperImpl(dumpManager) {
+
+    /**
+     * Whether bypassing the keyguard is enabled by the user in user settings (skipping the
+     * lockscreen when authenticating using secondary authentication types like face unlock).
+     */
+    val isBypassAvailable: Flow<Boolean> =
+        keyguardBypassRepository.isBypassAvailable.dumpWhileCollecting("isBypassAvailable")
+
+    /**
+     * Models whether bypass is unavailable (no secondary authentication types enrolled), or if the
+     * keyguard can be bypassed as a combination of the settings toggle value set by the user and
+     * other factors related to device state.
+     */
+    val canBypass: Flow<Boolean> =
+        isBypassAvailable
+            .flatMapLatest { isBypassAvailable ->
+                if (isBypassAvailable) {
+                    combine(
+                        sceneInteractor.currentScene.map { scene -> scene == Scenes.Bouncer },
+                        alternateBouncerInteractor.isVisible,
+                        sceneInteractor.currentScene.map { scene -> scene == Scenes.Lockscreen },
+                        keyguardQuickAffordanceInteractor.launchingAffordance,
+                        pulseExpansionInteractor.isPulseExpanding,
+                        shadeInteractor.isQsExpanded,
+                    ) {
+                        isBouncerShowing,
+                        isAlternateBouncerShowing,
+                        isOnLockscreenScene,
+                        isLaunchingAffordance,
+                        isPulseExpanding,
+                        isQsExpanded ->
+                        when {
+                            isBouncerShowing -> true
+                            isAlternateBouncerShowing -> true
+                            !isOnLockscreenScene -> false
+                            isLaunchingAffordance -> false
+                            isPulseExpanding -> false
+                            isQsExpanded -> false
+                            else -> true
+                        }
+                    }
+                } else {
+                    flowOf(false)
+                }
+            }
+            .dumpWhileCollecting("canBypass")
+
+    companion object {
+        private const val TAG: String = "KeyguardBypassInteractor"
+    }
+}
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/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index b24ca1a..2e0a160 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
@@ -284,7 +284,7 @@
         }
 
     /** Observable for the [StatusBarState] */
-    val statusBarState: Flow<StatusBarState> = repository.statusBarState
+    val statusBarState: StateFlow<StatusBarState> = repository.statusBarState
 
     /** Observable for [BiometricUnlockModel] when biometrics are used to unlock the device. */
     val biometricUnlockState: StateFlow<BiometricUnlockModel> = repository.biometricUnlockState
@@ -350,23 +350,21 @@
     val dismissAlpha: Flow<Float> =
         shadeRepository.legacyShadeExpansion
             .sampleCombine(
-                statusBarState,
                 keyguardTransitionInteractor.currentKeyguardState,
                 keyguardTransitionInteractor.transitionState,
                 isKeyguardDismissible,
                 keyguardTransitionInteractor.isFinishedIn(Scenes.Communal, GLANCEABLE_HUB),
             )
-            .filter { (_, _, _, step, _, _) -> !step.transitionState.isTransitioning() }
+            .filter { (_, _, step, _, _) -> !step.transitionState.isTransitioning() }
             .transform {
                 (
                     legacyShadeExpansion,
-                    statusBarState,
                     currentKeyguardState,
                     step,
                     isKeyguardDismissible,
                     onGlanceableHub) ->
                 if (
-                    statusBarState == StatusBarState.KEYGUARD &&
+                    statusBarState.value == StatusBarState.KEYGUARD &&
                         isKeyguardDismissible &&
                         currentKeyguardState == LOCKSCREEN &&
                         legacyShadeExpansion != 1f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 26bf26b..21afd3e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -61,6 +61,8 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
@@ -91,6 +93,11 @@
     @Application private val appContext: Context,
     private val sceneInteractor: Lazy<SceneInteractor>,
 ) {
+    /**
+     * Whether a quick affordance is being launched. Quick Affordances are interactive lockscreen UI
+     * elements that allow the user to perform quick actions without unlocking their device.
+     */
+    val launchingAffordance: StateFlow<Boolean> = repository.get().launchingAffordance.asStateFlow()
 
     /**
      * Whether the UI should use the long press gesture to activate quick affordances.
@@ -167,11 +174,7 @@
      * @param expandable An optional [Expandable] for the activity- or dialog-launch animation
      * @param slotId The id of the lockscreen slot that the affordance is in
      */
-    fun onQuickAffordanceTriggered(
-        configKey: String,
-        expandable: Expandable?,
-        slotId: String,
-    ) {
+    fun onQuickAffordanceTriggered(configKey: String, expandable: Expandable?, slotId: String) {
         val (decodedSlotId, decodedConfigKey) = configKey.decode()
         val config =
             repository.get().selections.value[decodedSlotId]?.find { it.key == decodedConfigKey }
@@ -191,10 +194,7 @@
                 )
             is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> Unit
             is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog ->
-                showDialog(
-                    result.dialog,
-                    result.expandable,
-                )
+                showDialog(result.dialog, result.expandable)
         }
     }
 
@@ -225,12 +225,7 @@
 
         selections.add(affordanceId)
 
-        repository
-            .get()
-            .setSelections(
-                slotId = slotId,
-                affordanceIds = selections,
-            )
+        repository.get().setSelections(slotId = slotId, affordanceIds = selections)
 
         logger.logQuickAffordanceSelected(slotId, affordanceId)
         metricsLogger.logOnShortcutSelected(slotId, affordanceId)
@@ -274,12 +269,7 @@
                 .getOrDefault(slotId, emptyList())
                 .toMutableList()
         return if (selections.remove(affordanceId)) {
-            repository
-                .get()
-                .setSelections(
-                    slotId = slotId,
-                    affordanceIds = selections,
-                )
+            repository.get().setSelections(slotId = slotId, affordanceIds = selections)
             true
         } else {
             false
@@ -399,11 +389,15 @@
                 intent,
                 true /* dismissShade */,
                 expandable?.activityTransitionController(),
-                true /* showOverLockscreenWhenLocked */,
+                true, /* showOverLockscreenWhenLocked */
             )
         }
     }
 
+    fun setLaunchingAffordance(isLaunchingAffordance: Boolean) {
+        repository.get().launchingAffordance.value = isLaunchingAffordance
+    }
+
     private fun String.encode(slotId: String): String {
         return "$slotId$DELIMITER$this"
     }
@@ -444,19 +438,19 @@
             ),
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_MONOCHROMATIC_THEME,
-                value = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEME)
+                value = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEME),
             ),
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_PICKER_UI_FOR_AIWP,
-                value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_UI_FOR_AIWP)
+                value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_UI_FOR_AIWP),
             ),
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_PAGE_TRANSITIONS,
-                value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PAGE_TRANSITIONS)
+                value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PAGE_TRANSITIONS),
             ),
             KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_PICKER_PREVIEW_ANIMATION,
-                value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PREVIEW_ANIMATION)
+                value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PREVIEW_ANIMATION),
             ),
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PulseExpansionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PulseExpansionInteractor.kt
new file mode 100644
index 0000000..377d7ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PulseExpansionInteractor.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.data.repository.PulseExpansionRepository
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+@SysUISingleton
+class PulseExpansionInteractor
+@Inject
+constructor(private val repository: PulseExpansionRepository, dumpManager: DumpManager) :
+    FlowDumperImpl(dumpManager) {
+    /**
+     * Whether the notification panel is expanding from the user swiping downward on a notification
+     * from the pulsing state, or swiping anywhere on the screen when face bypass is enabled
+     */
+    val isPulseExpanding: StateFlow<Boolean> =
+        repository.isPulseExpanding.asStateFlow().dumpValue("isPulseExpanding")
+
+    /** Updates whether a pulse expansion is occurring. */
+    fun setPulseExpanding(pulseExpanding: Boolean) {
+        repository.isPulseExpanding.value = pulseExpanding
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
index e404f27..2e3a095 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
@@ -20,13 +20,13 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.shade.data.repository.FlingInfo
 import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.util.kotlin.Utils.Companion.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -39,8 +39,8 @@
 constructor(
     @Background backgroundScope: CoroutineScope,
     shadeRepository: ShadeRepository,
-    transitionInteractor: KeyguardTransitionInteractor,
-    keyguardInteractor: KeyguardInteractor,
+    private val transitionInteractor: KeyguardTransitionInteractor,
+    private val keyguardInteractor: KeyguardInteractor,
 ) {
     /**
      * Emits a [FlingInfo] whenever a swipe to dismiss gesture has started a fling animation on the
@@ -50,20 +50,15 @@
      * LOCKSCREEN -> GONE, and by [KeyguardSurfaceBehindInteractor] to match the surface remote
      * animation's velocity to the fling velocity, if applicable.
      */
-    val dismissFling =
+    val dismissFling: StateFlow<FlingInfo?> =
         shadeRepository.currentFling
-            .sample(
-                transitionInteractor.startedKeyguardTransitionStep,
-                keyguardInteractor.isKeyguardDismissible,
-                keyguardInteractor.statusBarState,
-            )
-            .filter { (flingInfo, startedStep, keyguardDismissable, statusBarState) ->
+            .filter { flingInfo ->
                 flingInfo != null &&
                     !flingInfo.expand &&
-                    statusBarState != StatusBarState.SHADE_LOCKED &&
-                    startedStep.to == KeyguardState.LOCKSCREEN &&
-                    keyguardDismissable
+                    keyguardInteractor.statusBarState.value != StatusBarState.SHADE_LOCKED &&
+                    transitionInteractor.startedKeyguardTransitionStep.value.to ==
+                        KeyguardState.LOCKSCREEN &&
+                    keyguardInteractor.isKeyguardDismissible.value
             }
-            .map { (flingInfo, _) -> flingInfo }
             .stateIn(backgroundScope, SharingStarted.Eagerly, null)
 }
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/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 40d4193..0d81604 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -81,6 +82,7 @@
     private val communalInteractor: CommunalInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
+    private val pulseExpansionInteractor: PulseExpansionInteractor,
     notificationShadeWindowModel: NotificationShadeWindowModel,
     private val aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
     private val alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
@@ -371,7 +373,7 @@
 
     /** Is there an expanded pulse, are we animating in response? */
     private fun isPulseExpandingAnimated(): Flow<AnimatedValue<Boolean>> {
-        return notificationsKeyguardInteractor.isPulseExpanding
+        return pulseExpansionInteractor.isPulseExpanding
             .pairwise(initialValue = null)
             // If pulsing changes, start animating, unless it's the first emission
             .map { (prev, expanding) -> AnimatableEvent(expanding, startAnimating = prev != null) }
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/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..47dacae 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;
@@ -130,7 +131,9 @@
 
         // This activity is launched directly by an app, or system server. System server provides
         // the package name through the intent if so.
-        if (mPackageName == null) {
+        if (mPackageName == null || (
+                com.android.systemui.Flags.mediaProjectionRequestAttributionFix()
+                        && getCallingPackage() == null)) {
             if (launchingIntent.hasExtra(EXTRA_PACKAGE_REUSING_GRANTED_CONSENT)) {
                 mPackageName = launchingIntent.getStringExtra(
                         EXTRA_PACKAGE_REUSING_GRANTED_CONSENT);
@@ -158,8 +161,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 +285,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 +376,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/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/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 0b37b5b..1ca3927 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -1146,4 +1146,4 @@
             updateState();
         }
     }
-}
+}
\ No newline at end of file
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..bacff99 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -38,12 +38,14 @@
 import androidx.compose.animation.fadeOut
 import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.layout.Arrangement.spacedBy
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.navigationBars
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.windowInsetsPadding
@@ -59,6 +61,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
@@ -70,8 +75,10 @@
 import androidx.compose.ui.semantics.customActions
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.util.fastRoundToInt
+import androidx.compose.ui.viewinterop.AndroidView
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.lifecycleScope
@@ -94,10 +101,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.lifecycle.setSnapshotBinding
-import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
 import com.android.systemui.media.controls.ui.view.MediaHost
-import com.android.systemui.media.dagger.MediaModule.QS_PANEL
-import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.plugins.qs.QSContainerController
 import com.android.systemui.qs.composefragment.SceneKeys.QuickQuickSettings
@@ -123,7 +127,6 @@
 import java.io.PrintWriter
 import java.util.function.Consumer
 import javax.inject.Inject
-import javax.inject.Named
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
@@ -131,7 +134,7 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.launch
 
 @SuppressLint("ValidFragment")
 class QSFragmentCompose
@@ -139,21 +142,21 @@
 constructor(
     private val qsFragmentComposeViewModelFactory: QSFragmentComposeViewModel.Factory,
     private val dumpManager: DumpManager,
-    @Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost,
-    @Named(QS_PANEL) private val qsMediaHost: MediaHost,
 ) : LifecycleFragment(), QS, Dumpable {
 
     private val scrollListener = MutableStateFlow<QS.ScrollListener?>(null)
+    private val collapsedMediaVisibilityChangedListener =
+        MutableStateFlow<(Consumer<Boolean>)?>(null)
     private val heightListener = MutableStateFlow<QS.HeightListener?>(null)
     private val qsContainerController = MutableStateFlow<QSContainerController?>(null)
 
     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 =
@@ -180,8 +183,6 @@
         QSComposeFragment.isUnexpectedlyInLegacyMode()
         viewModel = qsFragmentComposeViewModelFactory.create(lifecycleScope)
 
-        qqsMediaHost.init(MediaHierarchyManager.LOCATION_QQS)
-        qsMediaHost.init(MediaHierarchyManager.LOCATION_QS)
         setListenerCollections()
         lifecycleScope.launch { viewModel.activate() }
     }
@@ -247,7 +248,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 +329,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 {
@@ -465,7 +489,7 @@
     }
 
     override fun setCollapsedMediaVisibilityChangedListener(listener: Consumer<Boolean>?) {
-        // TODO (b/353253280)
+        collapsedMediaVisibilityChangedListener.value = listener
     }
 
     override fun setScrollListener(scrollListener: QS.ScrollListener?) {
@@ -508,6 +532,7 @@
             lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 this@QSFragmentCompose.view?.setSnapshotBinding {
                     scrollListener.value?.onQsPanelScrollChanged(scrollState.value)
+                    collapsedMediaVisibilityChangedListener.value?.accept(viewModel.qqsMediaVisible)
                 }
                 launch {
                     setListenerJob(
@@ -543,7 +568,7 @@
                 .squishiness
                 .collectAsStateWithLifecycle()
 
-        Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) {
+        Column(modifier = Modifier.sysuiResTag(ResIdTags.quickQsPanel)) {
             Box(
                 modifier =
                     Modifier.fillMaxWidth()
@@ -555,6 +580,9 @@
                                 leftFromRoot + coordinates.size.width,
                                 topFromRoot + coordinates.size.height,
                             )
+                            if (squishiness == 1f) {
+                                viewModel.qqsHeight = coordinates.size.height
+                            }
                         }
                         // Use an approach layout to determien the height without squishiness, as
                         // that's the value that NPVC and QuickSettingsController care about
@@ -569,8 +597,7 @@
                         .padding(top = { qqsPadding }, bottom = { bottomPadding })
             ) {
                 if (viewModel.isQsEnabled) {
-                    QuickQuickSettings(
-                        viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel,
+                    Column(
                         modifier =
                             Modifier.collapseExpandSemanticAction(
                                     stringResource(
@@ -582,7 +609,16 @@
                                         QuickSettingsShade.Dimensions.Padding.roundToPx()
                                     }
                                 ),
-                    )
+                        verticalArrangement =
+                            spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
+                    ) {
+                        QuickQuickSettings(
+                            viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel
+                        )
+                        if (viewModel.qqsMediaVisible) {
+                            MediaObject(mediaHost = viewModel.qqsMediaHost)
+                        }
+                    }
                 }
             }
             Spacer(modifier = Modifier.weight(1f))
@@ -619,24 +655,38 @@
                                 }
                                 .onSizeChanged { viewModel.qsScrollHeight = it.height }
                                 .verticalScroll(scrollState)
+                                .sysuiResTag(ResIdTags.qsScroll)
                     ) {
                         Spacer(
                             modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() }
                         )
                         QuickSettingsLayout(
                             viewModel = viewModel.containerViewModel,
-                            modifier = Modifier.sysuiResTag("quick_settings_panel"),
+                            modifier = Modifier.sysuiResTag(ResIdTags.quickSettingsPanel),
                         )
+                        Spacer(modifier = Modifier.height(8.dp))
+                        if (viewModel.qsMediaVisible) {
+                            MediaObject(
+                                mediaHost = viewModel.qsMediaHost,
+                                modifier =
+                                    Modifier.padding(
+                                        horizontal = {
+                                            QuickSettingsShade.Dimensions.Padding.roundToPx()
+                                        }
+                                    ),
+                            )
+                        }
                     }
                 }
-            }
-            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(ResIdTags.qsFooterActions)
+                                .element(ElementKeys.FooterActions),
+                    )
+                }
             }
         }
     }
@@ -871,3 +921,45 @@
         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
+    }
+
+@Composable
+private fun MediaObject(mediaHost: MediaHost, modifier: Modifier = Modifier) {
+    Box {
+        AndroidView(
+            modifier = modifier,
+            factory = {
+                mediaHost.hostView.apply {
+                    layoutParams =
+                        FrameLayout.LayoutParams(
+                            FrameLayout.LayoutParams.MATCH_PARENT,
+                            FrameLayout.LayoutParams.WRAP_CONTENT,
+                        )
+                }
+            },
+            onReset = {},
+        )
+    }
+}
+
+private object ResIdTags {
+    const val quickSettingsPanel = "quick_settings_panel"
+    const val quickQsPanel = "quick_qs_panel"
+    const val qsScroll = "expanded_qs_scroll_view"
+    const val qsFooterActions = "qs_footer_actions"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
new file mode 100644
index 0000000..5127320
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.composefragment.dagger
+
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.util.Utils
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+
+@Module
+interface QSFragmentComposeModule {
+
+    companion object {
+        const val QS_USING_MEDIA_PLAYER = "compose_fragment_using_media_player"
+
+        @Provides
+        @SysUISingleton
+        @Named(QS_USING_MEDIA_PLAYER)
+        fun providesUsingMedia(@Application context: Context): Boolean {
+            return Utils.useQsMediaPlayer(context)
+        }
+    }
+}
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..0ca621d 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
@@ -37,11 +38,17 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.dagger.MediaModule.QS_PANEL
+import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.qs.panels.domain.interactor.TileSquishinessInteractor
-import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.InFirstPageViewModel
 import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
@@ -59,19 +66,22 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import java.io.PrintWriter
+import javax.inject.Named
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOf
 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,
@@ -83,10 +93,15 @@
     configurationInteractor: ConfigurationInteractor,
     private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
     private val squishinessInteractor: TileSquishinessInteractor,
-    private val paginatedGridViewModel: PaginatedGridViewModel,
+    private val inFirstPageViewModel: InFirstPageViewModel,
+    @Named(QUICK_QS_PANEL) val qqsMediaHost: MediaHost,
+    @Named(QS_PANEL) val qsMediaHost: MediaHost,
+    @Named(QSFragmentComposeModule.QS_USING_MEDIA_PLAYER) private val usingMedia: Boolean,
     @Assisted private val lifecycleScope: LifecycleCoroutineScope,
 ) : Dumpable, ExclusiveActivatable() {
 
+    val containerViewModel = containerViewModelFactory.create(true)
+
     private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator")
 
     val footerActionsViewModel =
@@ -189,7 +204,7 @@
     var collapseExpandAccessibilityAction: Runnable? = null
 
     val inFirstPage: Boolean
-        get() = paginatedGridViewModel.inFirstPage
+        get() = inFirstPageViewModel.inFirstPage
 
     var overScrollAmount by mutableStateOf(0)
 
@@ -220,6 +235,30 @@
         }
     }
 
+    val showingMirror: Boolean
+        get() = containerViewModel.brightnessSliderViewModel.showMirror
+
+    // The initial values in these two are not meaningful. The flow will emit on start the correct
+    // values. This is because we need to lazily fetch them after initMediaHosts.
+    val qqsMediaVisible by
+        hydrator.hydratedStateOf(
+            traceName = "qqsMediaVisible",
+            initialValue = usingMedia,
+            source =
+                if (usingMedia) {
+                    mediaHostVisible(qqsMediaHost)
+                } else {
+                    flowOf(false)
+                },
+        )
+
+    val qsMediaVisible by
+        hydrator.hydratedStateOf(
+            traceName = "qsMediaVisible",
+            initialValue = usingMedia,
+            source = if (usingMedia) mediaHostVisible(qsMediaHost) else flowOf(false),
+        )
+
     private var qsBounds by mutableStateOf(Rect())
 
     private val constrainedSquishinessFraction: Float
@@ -318,13 +357,28 @@
         )
 
     override suspend fun onActivated(): Nothing {
+        initMediaHosts() // init regardless of using media (same as current QS).
         coroutineScope {
             launch { hydrateSquishinessInteractor() }
             launch { hydrator.activate() }
+            launch { containerViewModel.activate() }
             awaitCancellation()
         }
     }
 
+    private fun initMediaHosts() {
+        qqsMediaHost.apply {
+            expansion = MediaHostState.EXPANDED
+            showsOnlyActiveMedia = true
+            init(MediaHierarchyManager.LOCATION_QQS)
+        }
+        qsMediaHost.apply {
+            expansion = MediaHostState.EXPANDED
+            showsOnlyActiveMedia = false
+            init(MediaHierarchyManager.LOCATION_QS)
+        }
+    }
+
     private suspend fun hydrateSquishinessInteractor() {
         snapshotFlow { constrainedSquishinessFraction }
             .collect { squishinessInteractor.setSquishinessValue(it) }
@@ -367,6 +421,10 @@
                 println("qqsHeight", "${qqsHeight}px")
                 println("qsScrollHeight", "${qsScrollHeight}px")
             }
+            printSection("Media") {
+                println("qqsMediaVisible", qqsMediaVisible)
+                println("qsMediaVisible", qsMediaVisible)
+            }
         }
     }
 
@@ -384,3 +442,21 @@
 }
 
 private val SHORT_PARALLAX_AMOUNT = 0.1f
+
+/**
+ * Returns a flow to track the visibility of a [MediaHost]. The flow will emit on start the visible
+ * state of the view.
+ */
+private fun mediaHostVisible(mediaHost: MediaHost): Flow<Boolean> {
+    return callbackFlow {
+            val listener: (Boolean) -> Unit = { visible: Boolean -> trySend(visible) }
+            mediaHost.addVisibilityChangeListener(listener)
+
+            awaitClose { mediaHost.removeVisibilityChangeListener(listener) }
+        }
+        // Need to use this to set initial state because on creation of the media host, the
+        // view visibility is not in sync with [MediaHost.visible], which is what we track with
+        // the listener. The correct state is set as part of init, so we need to get the state
+        // lazily.
+        .onStart { emit(mediaHost.visible) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 29bcad4..94b8a3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -19,6 +19,7 @@
 import com.android.systemui.media.dagger.MediaModule;
 import com.android.systemui.qs.ReduceBrightColorsController;
 import com.android.systemui.qs.ReduceBrightColorsControllerImpl;
+import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule;
 import com.android.systemui.qs.external.QSExternalModule;
 import com.android.systemui.qs.panels.dagger.PanelsModule;
 import com.android.systemui.qs.pipeline.dagger.QSPipelineModule;
@@ -40,6 +41,7 @@
         includes = {
                 MediaModule.class,
                 PanelsModule.class,
+                QSFragmentComposeModule.class,
                 QSExternalModule.class,
                 QSFlagsModule.class,
                 QSHostModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index d55763a..6cc2cbc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -40,9 +40,9 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
 import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.FooterHeight
 import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.InterPageSpacing
@@ -54,7 +54,7 @@
 class PaginatedGridLayout
 @Inject
 constructor(
-    private val viewModel: PaginatedGridViewModel,
+    private val viewModelFactory: PaginatedGridViewModel.Factory,
     @PaginatedBaseLayoutType private val delegateGridLayout: PaginatableGridLayout,
 ) : GridLayout by delegateGridLayout {
     @Composable
@@ -63,13 +63,18 @@
         modifier: Modifier,
         editModeStart: () -> Unit,
     ) {
+        val viewModel =
+            rememberViewModel(traceName = "PaginatedGridLayout-TileGrid") {
+                viewModelFactory.create()
+            }
+
         DisposableEffect(tiles) {
             val token = Any()
             tiles.forEach { it.startListening(token) }
             onDispose { tiles.forEach { it.stopListening(token) } }
         }
-        val columns by viewModel.columns.collectAsStateWithLifecycle()
-        val rows by viewModel.rows.collectAsStateWithLifecycle()
+        val columns by viewModel.columns
+        val rows = viewModel.rows
 
         val pages =
             remember(tiles, columns, rows) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index 99a6cda..ca28ab3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -41,7 +41,8 @@
     viewModel: QuickQuickSettingsViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle()
+
+    val sizedTiles = viewModel.tileViewModels
     val tiles = sizedTiles.fastMap { it.tile }
     val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
     val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
@@ -52,7 +53,7 @@
         tiles.forEach { it.startListening(token) }
         onDispose { tiles.forEach { it.stopListening(token) } }
     }
-    val columns by viewModel.columns.collectAsStateWithLifecycle()
+    val columns = viewModel.columns
     var cellIndex = 0
     Box(modifier = modifier) {
         GridAnchor()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 978a353..d107222 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -18,12 +18,12 @@
 
 import android.graphics.drawable.Animatable
 import android.text.TextUtils
+import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
 import androidx.compose.animation.graphics.res.animatedVectorResource
 import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
 import androidx.compose.animation.graphics.vector.AnimatedImageVector
 import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
 import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -32,8 +32,9 @@
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
@@ -44,6 +45,7 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
 import androidx.compose.ui.graphics.Shape
@@ -57,7 +59,9 @@
 import androidx.compose.ui.semantics.stateDescription
 import androidx.compose.ui.semantics.toggleableState
 import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import com.android.compose.modifiers.size
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.Flags
 import com.android.systemui.common.shared.model.Icon
@@ -88,12 +92,14 @@
     ) {
         // Icon
         val longPressLabel = longPressLabel().takeIf { onLongClick != null }
+        val animatedBackgroundColor by
+            animateColorAsState(colors.iconBackground, label = "QSTileDualTargetBackgroundColor")
         Box(
             modifier =
                 Modifier.size(CommonTileDefaults.ToggleTargetSize).thenIf(toggleClick != null) {
                     Modifier.clip(iconShape)
                         .verticalSquish(squishiness)
-                        .background(colors.iconBackground)
+                        .drawBehind { drawRect(animatedBackgroundColor) }
                         .combinedClickable(
                             onClick = toggleClick!!,
                             onLongClick = onLongClick,
@@ -117,6 +123,7 @@
             SmallTileContent(
                 icon = icon,
                 color = colors.icon,
+                size = { CommonTileDefaults.LargeTileIconSize },
                 modifier = Modifier.align(Alignment.Center),
             )
         }
@@ -139,18 +146,22 @@
     modifier: Modifier = Modifier,
     accessibilityUiState: AccessibilityUiState? = null,
 ) {
+    val animatedLabelColor by animateColorAsState(colors.label, label = "QSTileLabelColor")
+    val animatedSecondaryLabelColor by
+        animateColorAsState(colors.secondaryLabel, label = "QSTileSecondaryLabelColor")
     Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) {
-        Text(
+        BasicText(
             label,
             style = MaterialTheme.typography.labelLarge,
-            color = colors.label,
+            color = { animatedLabelColor },
             maxLines = 1,
             overflow = TextOverflow.Ellipsis,
         )
         if (!TextUtils.isEmpty(secondaryLabel)) {
-            Text(
+            BasicText(
                 secondaryLabel ?: "",
-                color = colors.secondaryLabel,
+                color = { animatedSecondaryLabelColor },
+                maxLines = 1,
                 style = MaterialTheme.typography.bodyMedium,
                 modifier =
                     Modifier.thenIf(
@@ -170,9 +181,11 @@
     modifier: Modifier = Modifier,
     icon: Icon,
     color: Color,
+    size: () -> Dp = { CommonTileDefaults.IconSize },
     animateToEnd: Boolean = false,
 ) {
-    val iconModifier = modifier.size(CommonTileDefaults.IconSize)
+    val animatedColor by animateColorAsState(color, label = "QSTileIconColor")
+    val iconModifier = modifier.size({ size().roundToPx() }, { size().roundToPx() })
     val context = LocalContext.current
     val loadedDrawable =
         remember(icon, context) {
@@ -182,7 +195,7 @@
             }
         }
     if (loadedDrawable !is Animatable) {
-        Icon(icon = icon, tint = color, modifier = iconModifier)
+        Icon(icon = icon, tint = animatedColor, modifier = iconModifier)
     } else if (icon is Icon.Resource) {
         val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
         val painter =
@@ -198,14 +211,15 @@
         Image(
             painter = painter,
             contentDescription = icon.contentDescription?.load(),
-            colorFilter = ColorFilter.tint(color = color),
+            colorFilter = ColorFilter.tint(color = animatedColor),
             modifier = iconModifier,
         )
     }
 }
 
 object CommonTileDefaults {
-    val IconSize = 24.dp
+    val IconSize = 32.dp
+    val LargeTileIconSize = 28.dp
     val ToggleTargetSize = 56.dp
     val TileHeight = 72.dp
     val TilePadding = 8.dp
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 418ed0b..b5cec12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -20,6 +20,7 @@
 
 import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.animation.fadeIn
@@ -54,7 +55,6 @@
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.ArrowBack
-import androidx.compose.material.icons.filled.ArrowBack
 import androidx.compose.material.icons.filled.Clear
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
@@ -69,21 +69,22 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 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
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
-import androidx.compose.ui.BiasAlignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.layout
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.layout.positionInRoot
@@ -103,6 +104,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.util.fastMap
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.bounceable
 import com.android.compose.modifiers.height
 import com.android.systemui.common.ui.compose.load
@@ -134,9 +136,10 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.shared.model.groupAndSort
 import com.android.systemui.res.R
+import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.collectLatest
 
 object TileType
 
@@ -222,7 +225,7 @@
                         if (dragIsInProgress) {
                             RemoveTileTarget()
                         } else {
-                            Text(text = "Hold and drag to rearrange tiles.")
+                            Text(text = stringResource(id = R.string.drag_to_rearrange_tiles))
                         }
                     }
                 }
@@ -240,7 +243,9 @@
                             spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
                         modifier = modifier.fillMaxSize(),
                     ) {
-                        EditGridHeader { Text(text = "Hold and drag to add tiles.") }
+                        EditGridHeader {
+                            Text(text = stringResource(id = R.string.drag_to_add_tiles))
+                        }
 
                         AvailableTileGrid(otherTiles, selectionState, columns, listState)
                     }
@@ -286,7 +291,7 @@
                 .padding(10.dp),
     ) {
         Icon(imageVector = Icons.Default.Clear, contentDescription = null)
-        Text(text = "Remove")
+        Text(text = stringResource(id = R.string.qs_customize_remove))
     }
 }
 
@@ -409,7 +414,7 @@
 /**
  * Adds a list of [GridCell] to the lazy grid
  *
- * @param cells the pairs of [GridCell] to [BounceableTileViewModel]
+ * @param cells the pairs of [GridCell] to [AnimatableTileViewModel]
  * @param dragAndDropState the [DragAndDropState] for this grid
  * @param selectionState the [MutableSelectionState] for this grid
  * @param onToggleSize the callback when a tile's size is toggled
@@ -545,9 +550,27 @@
                     selectionState::unSelect,
                 )
                 .tileBackground(colors.background)
-                .tilePadding()
         ) {
-            EditTile(tile = cell.tile, iconOnly = cell.isIcon)
+            val targetValue = if (cell.isIcon) 0f else 1f
+            val animatedProgress = remember { Animatable(targetValue) }
+
+            if (selected) {
+                val resizingState = selectionState.resizingState
+                LaunchedEffect(targetValue, resizingState) {
+                    if (resizingState == null) {
+                        animatedProgress.animateTo(targetValue)
+                    } else {
+                        snapshotFlow { resizingState.progression }
+                            .collectLatest { animatedProgress.snapTo(it) }
+                    }
+                }
+            }
+
+            EditTile(
+                tile = cell.tile,
+                tileWidths = { tileWidths },
+                progress = { animatedProgress.value },
+            )
         }
     }
 }
@@ -612,45 +635,72 @@
 }
 
 @Composable
-fun BoxScope.EditTile(
+fun EditTile(
     tile: EditTileViewModel,
-    iconOnly: Boolean,
+    tileWidths: () -> TileWidths?,
+    progress: () -> Float,
     colors: TileColors = EditModeTileDefaults.editTileColors(),
 ) {
-    // Animated horizontal alignment from center (0f) to start (-1f)
-    val alignmentValue by
-        animateFloatAsState(
-            targetValue = if (iconOnly) 0f else -1f,
-            label = "QSEditTileContentAlignment",
-        )
-    val alignment by remember {
-        derivedStateOf { BiasAlignment(horizontalBias = alignmentValue, verticalBias = 0f) }
-    }
-    // Icon
-    Box(Modifier.size(ToggleTargetSize).align(alignment)) {
-        SmallTileContent(
-            icon = tile.icon,
-            color = colors.icon,
-            animateToEnd = true,
-            modifier = Modifier.align(Alignment.Center),
-        )
-    }
+    val iconSizeDiff = CommonTileDefaults.IconSize - CommonTileDefaults.LargeTileIconSize
+    Row(
+        horizontalArrangement = spacedBy(6.dp),
+        verticalAlignment = Alignment.CenterVertically,
+        modifier =
+            Modifier.layout { measurable, constraints ->
+                    // Always display the tile using the large size and trust the parent composable
+                    // to clip the content as needed. This stop the labels from being truncated.
+                    val width = tileWidths()?.max ?: constraints.maxWidth
+                    val placeable =
+                        measurable.measure(constraints.copy(minWidth = width, maxWidth = width))
+                    val currentProgress = progress()
+                    val startPadding =
+                        if (currentProgress == 0f) {
+                            // Find the center of the max width when the tile is icon only
+                            iconHorizontalCenter(constraints.maxWidth)
+                        } else {
+                            // Find the center of the minimum width to hold the same position as the
+                            // tile is resized.
+                            val basePadding =
+                                tileWidths()?.min?.let { iconHorizontalCenter(it) } ?: 0f
+                            // Large tiles, represented with a progress of 1f, have a 0.dp padding
+                            basePadding * (1f - currentProgress)
+                        }
 
-    // Labels, positioned after the icon
-    AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) {
+                    layout(constraints.maxWidth, constraints.maxHeight) {
+                        placeable.place(startPadding.roundToInt(), 0)
+                    }
+                }
+                .tilePadding(),
+    ) {
+        // Icon
+        Box(Modifier.size(ToggleTargetSize)) {
+            SmallTileContent(
+                icon = tile.icon,
+                color = colors.icon,
+                animateToEnd = true,
+                size = { CommonTileDefaults.IconSize - iconSizeDiff * progress() },
+                modifier = Modifier.align(Alignment.Center),
+            )
+        }
+
+        // Labels, positioned after the icon
         LargeTileLabels(
             label = tile.label.text,
             secondaryLabel = tile.appName?.text,
             colors = colors,
-            modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding),
+            modifier = Modifier.weight(1f).graphicsLayer { alpha = progress() },
         )
     }
 }
 
+private fun MeasureScope.iconHorizontalCenter(containerSize: Int): Float {
+    return (containerSize - ToggleTargetSize.roundToPx()) / 2f -
+        CommonTileDefaults.TilePadding.toPx()
+}
+
 private fun Modifier.tileBackground(color: Color): Modifier {
-    return drawBehind {
-        drawRoundRect(SolidColor(color), cornerRadius = CornerRadius(InactiveCornerRadius.toPx()))
-    }
+    // Clip tile contents from overflowing past the tile
+    return clip(RoundedCornerShape(InactiveCornerRadius)).drawBehind { drawRect(color) }
 }
 
 private object EditModeTileDefaults {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 91f2da2..19ab29e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -72,7 +72,7 @@
             rememberViewModel(traceName = "InfiniteGridLayout.TileGrid") {
                 viewModel.dynamicIconTilesViewModelFactory.create()
             }
-        val columns by viewModel.gridSizeViewModel.columns.collectAsStateWithLifecycle()
+        val columns by viewModel.gridSizeViewModel.columns
         val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
         val bounceables =
             remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
@@ -118,7 +118,7 @@
             rememberViewModel(traceName = "InfiniteGridLayout.EditTileGrid") {
                 viewModel.dynamicIconTilesViewModelFactory.create()
             }
-        val columns by viewModel.gridSizeViewModel.columns.collectAsStateWithLifecycle()
+        val columns by viewModel.gridSizeViewModel.columns
         val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle()
 
         // Non-current tiles should always be displayed as icon tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index e1583e3..5bebdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -21,6 +21,7 @@
 import android.content.res.Resources
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.service.quicksettings.Tile.STATE_INACTIVE
+import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.combinedClickable
@@ -61,6 +62,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.Expandable
 import com.android.compose.animation.bounceable
 import com.android.compose.modifiers.thenIf
@@ -74,6 +76,7 @@
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.panels.ui.compose.BounceableInfo
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight
 import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
 import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
@@ -82,7 +85,6 @@
 import com.android.systemui.res.R
 import java.util.function.Supplier
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 private const val TEST_TAG_SMALL = "qs_tile_small"
 private const val TEST_TAG_LARGE = "qs_tile_large"
@@ -128,14 +130,18 @@
 
     // TODO(b/361789146): Draw the shapes instead of clipping
     val tileShape = TileDefaults.animateTileShape(uiState.state)
-
-    TileExpandable(
-        color =
+    val animatedColor by
+        animateColorAsState(
             if (iconOnly || !uiState.handlesSecondaryClick) {
                 colors.iconBackground
             } else {
                 colors.background
             },
+            label = "QSTileBackgroundColor",
+        )
+
+    TileExpandable(
+        color = { animatedColor },
         shape = tileShape,
         squishiness = squishiness,
         hapticsViewModel = hapticsViewModel,
@@ -212,7 +218,7 @@
 
 @Composable
 private fun TileExpandable(
-    color: Color,
+    color: () -> Color,
     shape: Shape,
     squishiness: () -> Float,
     hapticsViewModel: TileHapticsViewModel?,
@@ -220,7 +226,7 @@
     content: @Composable (Expandable) -> Unit,
 ) {
     Expandable(
-        color = color,
+        color = color(),
         shape = shape,
         modifier = modifier.clip(shape).verticalSquish(squishiness),
     ) {
@@ -238,7 +244,7 @@
 ) {
     Box(
         modifier =
-            Modifier.height(CommonTileDefaults.TileHeight)
+            Modifier.height(TileHeight)
                 .fillMaxWidth()
                 .tileCombinedClickable(
                     onClick = onClick,
@@ -336,6 +342,16 @@
         )
 
     @Composable
+    fun inactiveDualTargetTileColors(): TileColors =
+        TileColors(
+            background = MaterialTheme.colorScheme.surfaceVariant,
+            iconBackground = MaterialTheme.colorScheme.surfaceContainerHighest,
+            label = MaterialTheme.colorScheme.onSurfaceVariant,
+            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
+            icon = MaterialTheme.colorScheme.onSurfaceVariant,
+        )
+
+    @Composable
     fun inactiveTileColors(): TileColors =
         TileColors(
             background = MaterialTheme.colorScheme.surfaceVariant,
@@ -365,7 +381,13 @@
                     activeTileColors()
                 }
             }
-            STATE_INACTIVE -> inactiveTileColors()
+            STATE_INACTIVE -> {
+                if (uiState.handlesSecondaryClick) {
+                    inactiveDualTargetTileColors()
+                } else {
+                    inactiveTileColors()
+                }
+            }
             else -> unavailableTileColors()
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt
index a084bc2..9552aa9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt
@@ -17,25 +17,30 @@
 package com.android.systemui.qs.panels.ui.compose.selection
 
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.setValue
 import com.android.systemui.qs.panels.ui.compose.selection.ResizingDefaults.RESIZING_THRESHOLD
 
 class ResizingState(private val widths: TileWidths, private val onResize: () -> Unit) {
-    // Total drag offset of this resize operation
-    private var totalOffset = 0f
+    /** Total drag offset of this resize operation. */
+    private var totalOffset by mutableFloatStateOf(0f)
 
     /** Width in pixels of the resizing tile. */
     var width by mutableIntStateOf(widths.base)
 
+    /** Progression between icon (0) and large (1) sizes. */
+    val progression
+        get() = calculateProgression()
+
     // Whether the tile is currently over the threshold and should be a large tile
-    private var passedThreshold: Boolean = passedThreshold(calculateProgression(width))
+    private var passedThreshold: Boolean = passedThreshold(progression)
 
     fun onDrag(offset: Float) {
         totalOffset += offset
         width = (widths.base + totalOffset).toInt().coerceIn(widths.min, widths.max)
 
-        passedThreshold(calculateProgression(width)).let {
+        passedThreshold(progression).let {
             // Resize if we went over the threshold
             if (passedThreshold != it) {
                 passedThreshold = it
@@ -49,7 +54,7 @@
     }
 
     /** The progression of the resizing tile between an icon tile (0f) and a large tile (1f) */
-    private fun calculateProgression(width: Int): Float {
+    private fun calculateProgression(): Float {
         return ((width - widths.min) / (widths.max - widths.min).toFloat()).coerceIn(0f, 1f)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index 9f13a37..8a345ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -16,7 +16,11 @@
 
 package com.android.systemui.qs.panels.ui.compose.selection
 
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.animation.core.animateIntAsState
+import androidx.compose.animation.core.spring
 import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.gestures.detectHorizontalDragGestures
 import androidx.compose.foundation.layout.Box
@@ -78,7 +82,6 @@
         ResizingHandle(
             enabled = selected,
             selectionState = selectionState,
-            transition = selectionAlpha,
             tileWidths = tileWidths,
             modifier =
                 // Higher zIndex to make sure the handle is drawn above the content
@@ -91,7 +94,6 @@
 private fun ResizingHandle(
     enabled: Boolean,
     selectionState: MutableSelectionState,
-    transition: () -> Float,
     tileWidths: () -> TileWidths?,
     modifier: Modifier = Modifier,
 ) {
@@ -126,19 +128,24 @@
                     }
             }
     ) {
-        ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center))
+        ResizingDot(enabled = enabled, modifier = Modifier.align(Alignment.Center))
     }
 }
 
 @Composable
 private fun ResizingDot(
-    transition: () -> Float,
+    enabled: Boolean,
     modifier: Modifier = Modifier,
     color: Color = MaterialTheme.colorScheme.primary,
 ) {
+    val alpha by animateFloatAsState(if (enabled) 1f else 0f)
+    val radius by
+        animateDpAsState(
+            if (enabled) ResizingDotSize / 2 else 0.dp,
+            animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy),
+        )
     Canvas(modifier = modifier.size(ResizingDotSize)) {
-        val v = transition()
-        drawCircle(color = color, radius = (ResizingDotSize / 2).toPx() * v, alpha = v)
+        drawCircle(color = color, radius = radius.toPx(), alpha = alpha)
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegate.kt
index 03fc425..cbece2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegate.kt
@@ -23,7 +23,7 @@
 import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.LifecycleOwner
 import com.android.compose.PlatformButton
-import com.android.compose.PlatformOutlinedButton
+import com.android.compose.PlatformTextButton
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dialog.ui.composable.AlertDialogContent
 import com.android.systemui.qs.panels.domain.interactor.EditTilesResetInteractor
@@ -84,8 +84,8 @@
                     Text(stringResource(id = android.R.string.ok))
                 }
             },
-            neutralButton = {
-                PlatformOutlinedButton(onClick = { dialog.dismiss() }) {
+            negativeButton = {
+                PlatformTextButton(onClick = { dialog.dismiss() }) {
                     Text(stringResource(id = android.R.string.cancel))
                 }
             },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModel.kt
similarity index 60%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModel.kt
index 06592b1..225f542 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModel.kt
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.qs.panels.ui.viewmodel
 
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
+/*
+ * Tracks whether the current HorizontalPager (using this viewmodel) is in the first page.
+ * This requires it to be a `@SysUISingleton` to be shared between viewmodels.
+ */
+@SysUISingleton
+class InFirstPageViewModel @Inject constructor() {
+    var inFirstPage = true
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
index 0d12067..d687100 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.panels.ui.viewmodel
 
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.qs.panels.ui.dialog.QSResetDialogDelegate
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -27,12 +28,16 @@
     val gridSizeViewModel: QSColumnsViewModel,
     val squishinessViewModel: TileSquishinessViewModel,
     private val resetDialogDelegate: QSResetDialogDelegate,
-) {
+) : ExclusiveActivatable() {
 
     fun showResetDialog() {
         resetDialogDelegate.showDialog()
     }
 
+    override suspend fun onActivated(): Nothing {
+        gridSizeViewModel.activate()
+    }
+
     @AssistedFactory
     interface Factory {
         fun create(): InfiniteGridViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
index 0f7dafc..8bd9ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -16,33 +16,50 @@
 
 package com.android.systemui.qs.panels.ui.viewmodel
 
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.qs.panels.domain.interactor.PaginatedGridInteractor
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.stateIn
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
 
-@SysUISingleton
 class PaginatedGridViewModel
-@Inject
+@AssistedInject
 constructor(
     iconTilesViewModel: IconTilesViewModel,
-    gridSizeViewModel: QSColumnsViewModel,
+    private val gridSizeViewModel: QSColumnsViewModel,
     paginatedGridInteractor: PaginatedGridInteractor,
-    @Application applicationScope: CoroutineScope,
-) : IconTilesViewModel by iconTilesViewModel, QSColumnsViewModel by gridSizeViewModel {
-    val rows =
-        paginatedGridInteractor.rows.stateIn(
-            applicationScope,
-            SharingStarted.WhileSubscribed(),
-            paginatedGridInteractor.defaultRows,
+    inFirstPageViewModel: InFirstPageViewModel,
+) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() {
+
+    private val hydrator = Hydrator("PaginatedGridViewModel")
+
+    val rows by
+        hydrator.hydratedStateOf(
+            traceName = "rows",
+            initialValue = paginatedGridInteractor.defaultRows,
+            source = paginatedGridInteractor.rows,
         )
 
-    /*
-     * Tracks whether the current HorizontalPager (using this viewmodel) is in the first page.
-     * This requires it to be a `@SysUISingleton` to be shared between viewmodels.
-     */
-    var inFirstPage = true
+    var inFirstPage by inFirstPageViewModel::inFirstPage
+
+    val columns: State<Int>
+        get() = gridSizeViewModel.columns
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch { hydrator.activate() }
+            launch { gridSizeViewModel.activate() }
+            awaitCancellation()
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): PaginatedGridViewModel
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
index 0f1c77e..8926d2f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QSColumnsViewModel.kt
@@ -16,17 +16,25 @@
 
 package com.android.systemui.qs.panels.ui.viewmodel
 
-import com.android.systemui.dagger.SysUISingleton
+import androidx.compose.runtime.State
+import com.android.systemui.lifecycle.Activatable
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.qs.panels.domain.interactor.QSColumnsInteractor
 import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
 
-interface QSColumnsViewModel {
-    val columns: StateFlow<Int>
+interface QSColumnsViewModel : Activatable {
+    val columns: State<Int>
 }
 
-@SysUISingleton
 class QSColumnsSizeViewModelImpl @Inject constructor(interactor: QSColumnsInteractor) :
-    QSColumnsViewModel {
-    override val columns: StateFlow<Int> = interactor.columns
+    QSColumnsViewModel, ExclusiveActivatable() {
+    private val hydrator = Hydrator("QSColumnsSizeViewModelImpl")
+
+    override val columns =
+        hydrator.hydratedStateOf(traceName = "columns", source = interactor.columns)
+
+    override suspend fun onActivated(): Nothing {
+        hydrator.activate()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
index 887a70f..0859c86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt
@@ -16,67 +16,69 @@
 
 package com.android.systemui.qs.panels.ui.viewmodel
 
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
 import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.qs.panels.domain.interactor.QuickQuickSettingsRowInteractor
-import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
 import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.stateIn
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
 
-@OptIn(ExperimentalCoroutinesApi::class)
-@SysUISingleton
 class QuickQuickSettingsViewModel
-@Inject
+@AssistedInject
 constructor(
     tilesInteractor: CurrentTilesInteractor,
-    qsColumnsViewModel: QSColumnsViewModel,
+    private val qsColumnsViewModel: QSColumnsViewModel,
     quickQuickSettingsRowInteractor: QuickQuickSettingsRowInteractor,
     val squishinessViewModel: TileSquishinessViewModel,
-    private val iconTilesViewModel: IconTilesViewModel,
-    @Application private val applicationScope: CoroutineScope,
+    iconTilesViewModel: IconTilesViewModel,
     val tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider,
-) {
+) : ExclusiveActivatable() {
 
-    val columns = qsColumnsViewModel.columns
+    private val hydrator = Hydrator("QuickQuickSettingsViewModel")
 
-    private val rows =
-        quickQuickSettingsRowInteractor.rows.stateIn(
-            applicationScope,
-            SharingStarted.WhileSubscribed(),
-            quickQuickSettingsRowInteractor.defaultRows,
+    val columns by qsColumnsViewModel.columns
+
+    private val largeTiles by
+        hydrator.hydratedStateOf(traceName = "largeTiles", source = iconTilesViewModel.largeTiles)
+
+    private val rows by
+        hydrator.hydratedStateOf(
+            traceName = "rows",
+            initialValue = quickQuickSettingsRowInteractor.defaultRows,
+            source = quickQuickSettingsRowInteractor.rows,
         )
 
-    val tileViewModels: StateFlow<List<SizedTile<TileViewModel>>> =
-        columns
-            .flatMapLatest { columns ->
-                tilesInteractor.currentTiles.combine(rows, ::Pair).mapLatest { (tiles, rows) ->
-                    tiles
-                        .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width) }
-                        .let { splitInRowsSequence(it, columns).take(rows).toList().flatten() }
-                }
-            }
-            .stateIn(
-                applicationScope,
-                SharingStarted.WhileSubscribed(),
-                tilesInteractor.currentTiles.value
-                    .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width) }
-                    .let {
-                        splitInRowsSequence(it, columns.value).take(rows.value).toList().flatten()
-                    },
-            )
+    private val currentTiles by
+        hydrator.hydratedStateOf(traceName = "currentTiles", source = tilesInteractor.currentTiles)
+
+    val tileViewModels by derivedStateOf {
+        currentTiles
+            .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width) }
+            .let { splitInRowsSequence(it, columns).take(rows).toList().flatten() }
+    }
 
     private val TileSpec.width: Int
-        get() = if (iconTilesViewModel.isIconTile(this)) 1 else 2
+        get() = if (largeTiles.contains(this)) 2 else 1
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch { hydrator.activate() }
+            launch { qsColumnsViewModel.activate() }
+            awaitCancellation()
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): QuickQuickSettingsViewModel
+    }
 }
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..da175c9 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,42 @@
 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
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
 
-@SysUISingleton
 class QuickSettingsContainerViewModel
-@Inject
+@AssistedInject
 constructor(
-    val brightnessSliderViewModel: BrightnessSliderViewModel,
+    brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory,
+    quickQuickSettingsViewModelFactory: QuickQuickSettingsViewModel.Factory,
+    @Assisted supportsBrightnessMirroring: Boolean,
     val tileGridViewModel: TileGridViewModel,
     val editModeViewModel: EditModeViewModel,
-    val quickQuickSettingsViewModel: QuickQuickSettingsViewModel,
-)
+) : ExclusiveActivatable() {
+
+    val brightnessSliderViewModel =
+        brightnessSliderViewModelFactory.create(supportsBrightnessMirroring)
+
+    val quickQuickSettingsViewModel = quickQuickSettingsViewModelFactory.create()
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch { brightnessSliderViewModel.activate() }
+            launch { quickQuickSettingsViewModel.activate() }
+            awaitCancellation()
+        }
+    }
+
+    @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/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 5229acc..f3c6190 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -19,6 +19,7 @@
 package com.android.systemui.scene.domain.startable
 
 import android.app.StatusBarManager
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.internal.logging.UiEventLogger
@@ -102,7 +103,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /**
  * Hooks up business logic that manipulates the state of the [SceneInteractor] for the system UI
@@ -300,7 +300,7 @@
             bouncerInteractor.onImeHiddenByUser.collectLatest {
                 if (sceneInteractor.currentScene.value == Scenes.Bouncer) {
                     sceneInteractor.changeScene(
-                        toScene = Scenes.Lockscreen, // TODO(b/336581871): add sceneState?
+                        toScene = Scenes.Lockscreen,
                         loggingReason = "IME hidden",
                     )
                 }
@@ -318,7 +318,6 @@
                     when {
                         isAnySimLocked -> {
                             switchToScene(
-                                // TODO(b/336581871): add sceneState?
                                 targetSceneKey = Scenes.Bouncer,
                                 loggingReason = "Need to authenticate locked SIM card.",
                             )
@@ -326,7 +325,6 @@
                         unlockStatus.isUnlocked &&
                             deviceEntryInteractor.canSwipeToEnter.value == false -> {
                             switchToScene(
-                                // TODO(b/336581871): add sceneState?
                                 targetSceneKey = Scenes.Gone,
                                 loggingReason =
                                     "All SIM cards unlocked and device already unlocked and " +
@@ -335,7 +333,6 @@
                         }
                         else -> {
                             switchToScene(
-                                // TODO(b/336581871): add sceneState?
                                 targetSceneKey = Scenes.Lockscreen,
                                 loggingReason =
                                     "All SIM cards unlocked and device still locked" +
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index cd38ca6..7d7cab4 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -77,6 +77,11 @@
         }
     }
 
+    override fun onTouchEvent(event: MotionEvent?): Boolean {
+        event?.let { motionEventHandler?.onEmptySpaceMotionEvent(it) }
+        return super.onTouchEvent(event)
+    }
+
     companion object {
         private const val TAG = "SceneWindowRootView"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 82f65cf..32d5cb4 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.scene.ui.composable.Overlay
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
@@ -48,7 +49,6 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Models UI state for the scene container. */
 class SceneContainerViewModel
@@ -58,6 +58,7 @@
     private val falsingInteractor: FalsingInteractor,
     private val powerInteractor: PowerInteractor,
     shadeInteractor: ShadeInteractor,
+    private val remoteInputInteractor: RemoteInputInteractor,
     private val splitEdgeDetector: SplitEdgeDetector,
     private val logger: SceneLogger,
     hapticsViewModelFactory: SceneContainerHapticsViewModel.Factory,
@@ -101,6 +102,10 @@
                         this@SceneContainerViewModel.onMotionEvent(motionEvent)
                     }
 
+                    override fun onEmptySpaceMotionEvent(motionEvent: MotionEvent) {
+                        this@SceneContainerViewModel.onEmptySpaceMotionEvent(motionEvent)
+                    }
+
                     override fun onMotionEventComplete() {
                         this@SceneContainerViewModel.onMotionEventComplete()
                     }
@@ -147,6 +152,23 @@
     }
 
     /**
+     * Notifies that a [MotionEvent] has propagated through the entire [SharedNotificationContainer]
+     * and Composable scene container hierarchy without being handled.
+     *
+     * Call this after the [MotionEvent] has finished propagating through the UI hierarchy.
+     */
+    fun onEmptySpaceMotionEvent(event: MotionEvent) {
+        // check if the touch is outside the window and if remote input is active.
+        // If true, close any active remote inputs.
+        if (
+            event.action == MotionEvent.ACTION_OUTSIDE &&
+                (remoteInputInteractor.isRemoteInputActive as StateFlow).value
+        ) {
+            remoteInputInteractor.closeRemoteInputs()
+        }
+    }
+
+    /**
      * Notifies that a scene container user interaction has begun.
      *
      * This is a user interaction that has reached the Composable hierarchy of the scene container,
@@ -263,6 +285,9 @@
         /** Notifies that a [MotionEvent] has occurred. */
         fun onMotionEvent(motionEvent: MotionEvent)
 
+        /** Notifies that a [MotionEvent] has occurred outside the root window. */
+        fun onEmptySpaceMotionEvent(motionEvent: MotionEvent)
+
         /**
          * Notifies that the previous [MotionEvent] reported by [onMotionEvent] has finished
          * processing.
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/CameraLauncher.java b/packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java
index fc61e90..1d81e40 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/CameraLauncher.java
@@ -18,25 +18,33 @@
 
 import com.android.systemui.camera.CameraGestureHelper;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 
+import dagger.Lazy;
+
 import javax.inject.Inject;
 
+
 /** Handles launching camera from Shade. */
 @SysUISingleton
 public class CameraLauncher {
     private final CameraGestureHelper mCameraGestureHelper;
     private final KeyguardBypassController mKeyguardBypassController;
+    private final Lazy<KeyguardQuickAffordanceInteractor> mKeyguardQuickAffordanceInteractorLazy;
 
     private boolean mLaunchingAffordance;
 
     @Inject
     public CameraLauncher(
             CameraGestureHelper cameraGestureHelper,
-            KeyguardBypassController keyguardBypassController
+            KeyguardBypassController keyguardBypassController,
+            Lazy<KeyguardQuickAffordanceInteractor> keyguardQuickAffordanceInteractorLazy
     ) {
         mCameraGestureHelper = cameraGestureHelper;
         mKeyguardBypassController = keyguardBypassController;
+        mKeyguardQuickAffordanceInteractorLazy = keyguardQuickAffordanceInteractorLazy;
     }
 
     /** Launches the camera. */
@@ -54,7 +62,12 @@
      */
     public void setLaunchingAffordance(boolean launchingAffordance) {
         mLaunchingAffordance = launchingAffordance;
-        mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
+        if (SceneContainerFlag.isEnabled()) {
+            mKeyguardQuickAffordanceInteractorLazy.get()
+                    .setLaunchingAffordance(launchingAffordance);
+        } else {
+            mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt
new file mode 100644
index 0000000..6fb3ca5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/LongPressGestureDetector.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.content.Context
+import android.view.GestureDetector
+import android.view.GestureDetector.SimpleOnGestureListener
+import android.view.MotionEvent
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Accepts touch events, detects long press, and calls ShadeViewController#onStatusBarLongPress. */
+@SysUISingleton
+class LongPressGestureDetector
+@Inject
+constructor(context: Context, val shadeViewController: ShadeViewController) {
+    val gestureDetector =
+        GestureDetector(
+            context,
+            object : SimpleOnGestureListener() {
+                override fun onLongPress(event: MotionEvent) {
+                    shadeViewController.onStatusBarLongPress(event)
+                }
+            },
+        )
+
+    /** Accepts touch events to detect long presses. */
+    fun handleTouch(ev: MotionEvent) {
+        gestureDetector.onTouchEvent(ev)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 083cf1f..0e82bf8 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;
@@ -363,6 +365,7 @@
     private final TouchHandler mTouchHandler = new TouchHandler();
 
     private long mDownTime;
+    private long mStatusBarLongPressDowntime;
     private boolean mTouchSlopExceededBeforeDown;
     private float mOverExpansion;
     private CentralSurfaces mCentralSurfaces;
@@ -664,6 +667,7 @@
             };
 
     private final ActivityStarter mActivityStarter;
+    private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor;
 
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
@@ -757,7 +761,8 @@
             PowerInteractor powerInteractor,
             KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
             NaturalScrollingSettingObserver naturalScrollingSettingObserver,
-            MSDLPlayer msdlPlayer) {
+            MSDLPlayer msdlPlayer,
+            BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor) {
         SceneContainerFlag.assertInLegacyMode();
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
@@ -944,6 +949,7 @@
                 },
                 mFalsingManager);
         mActivityStarter = activityStarter;
+        mBrightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor;
         onFinishInflate();
         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
                 new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@@ -1185,6 +1191,12 @@
                     }
                 },
                 mMainDispatcher);
+        if (QSComposeFragment.isEnabled()) {
+            collectFlow(mView,
+                    mBrightnessMirrorShowingInteractor.isShowing(),
+                    isShowing -> setAlpha(isShowing ? 0 : 255, true)
+            );
+        }
     }
 
     @VisibleForTesting
@@ -3087,6 +3099,25 @@
         }
     }
 
+    /** @deprecated Temporary a11y solution until dual shade launch b/371224114 */
+    @Override
+    @Deprecated
+    public void onStatusBarLongPress(MotionEvent event) {
+        mShadeLog.d("Status Bar was long pressed.");
+        ShadeExpandsOnStatusBarLongPress.assertInNewMode();
+        mStatusBarLongPressDowntime = event.getDownTime();
+        if (isTracking()) {
+            onTrackingStopped(true);
+        }
+        if (isExpanded() && !mQsController.getExpanded()) {
+            mShadeLog.d("Status Bar was long pressed. Expanding to QS.");
+            expandToQs();
+        } else {
+            mShadeLog.d("Status Bar was long pressed. Expanding to Notifications.");
+            expandToNotifications();
+        }
+    }
+
     @Override
     public int getBarState() {
         return mBarState;
@@ -3750,6 +3781,7 @@
     private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
         mShadeLog.logEndMotionEvent("endMotionEvent called", forceCancel, false);
         mTrackingPointer = -1;
+        mStatusBarLongPressDowntime = 0L;
         mAmbientState.setSwipingUp(false);
         if ((isTracking() && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
                 || Math.abs(y - mInitialExpandY) > mTouchSlop
@@ -5066,6 +5098,13 @@
                 }
                 return true;
             }
+            // This touch session has already resulted in shade expansion. Ignore everything else.
+            if (ShadeExpandsOnStatusBarLongPress.isEnabled()
+                    && event.getActionMasked() != MotionEvent.ACTION_DOWN
+                    && event.getDownTime() == mStatusBarLongPressDowntime) {
+                mShadeLog.d("Touch has same down time as Status Bar long press. Ignoring.");
+                return false;
+            }
             if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
                 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
                 handled = true;
@@ -5146,6 +5185,7 @@
                     mUpdateFlingOnLayout = false;
                     mMotionAborted = false;
                     mDownTime = mSystemClock.uptimeMillis();
+                    mStatusBarLongPressDowntime = 0L;
                     mTouchAboveFalsingThreshold = false;
                     mCollapsedAndHeadsUpOnDown =
                             isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
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/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 49fa80c..5b06ad2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade
 
 import android.view.MotionEvent
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.assist.AssistManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -39,7 +40,6 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.first
-import com.android.app.tracing.coroutines.launchTraced as launch
 import kotlinx.coroutines.withContext
 
 /**
@@ -136,7 +136,6 @@
     }
 
     private fun animateCollapseShadeInternal() {
-        // TODO(b/336581871): add sceneState?
         shadeInteractor.collapseEitherShade(
             loggingReason = "ShadeController.animateCollapseShade",
             transitionKey = SlightlyFasterShadeCollapse,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index daea977..b085aec 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -98,6 +98,10 @@
     /** Returns the ShadeHeadsUpTracker. */
     val shadeHeadsUpTracker: ShadeHeadsUpTracker
 
+    @Deprecated("Temporary a11y solution until dual shade launch b/371224114")
+    /** Notifies the shade that a status bar detected a long press gesture. */
+    fun onStatusBarLongPress(event: MotionEvent)
+
     /** Returns the ShadeFoldAnimator. */
     @Deprecated("This interface is deprecated in Scene Container")
     val shadeFoldAnimator: ShadeFoldAnimator
@@ -179,9 +183,7 @@
     /** Returns the expanded height of the panel view. */
     @Deprecated("deprecated by SceneContainerFlag.isEnabled") val panelViewExpandedHeight: Float
 
-    /**
-     * Returns true if heads up should be visible.
-     */
+    /** Returns true if heads up should be visible. */
     @Deprecated("deprecated by SceneContainerFlag.isEnabled.") fun shouldHeadsUpBeVisible(): Boolean
 
     /** Return the fraction of the shade that's expanded, when in lockscreen. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 9322d31..53617d0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -35,60 +35,95 @@
     ShadeLockscreenInteractor,
     PanelExpansionInteractor {
     @Deprecated("Use ShadeInteractor instead") override fun expandToNotifications() {}
+
     @Deprecated("Use ShadeInteractor instead") override val isExpanded: Boolean = false
     override val isPanelExpanded: Boolean = false
+
     override fun animateCollapseQs(fullyCollapse: Boolean) {}
+
     override fun canBeCollapsed(): Boolean = false
+
     @Deprecated("Use ShadeAnimationInteractor instead") override val isCollapsing: Boolean = false
     @Deprecated("Use !ShadeInteractor.isAnyExpanded instead")
     override val isFullyCollapsed: Boolean = false
     override val isTracking: Boolean = false
     override val isViewEnabled: Boolean = false
+
     override fun shouldHideStatusBarIconsWhenExpanded() = false
+
     @Deprecated("Not supported by scenes") override fun blockExpansionForCurrentTouch() {}
+
     override fun startExpandLatencyTracking() {}
+
     override fun startBouncerPreHideAnimation() {}
+
     override fun dozeTimeTick() {}
+
     override fun resetViews(animate: Boolean) {}
+
     override val barState: Int = 0
+
     @Deprecated("Only supported by very old devices that will not adopt scenes.")
     override fun closeUserSwitcherIfOpen(): Boolean {
         return false
     }
+
     override fun onBackPressed() {}
+
     @Deprecated("According to b/318376223, shade predictive back is not be supported.")
     override fun onBackProgressed(progressFraction: Float) {}
+
     override fun setAlpha(alpha: Int, animate: Boolean) {}
+
     override fun setAlphaChangeAnimationEndAction(r: Runnable) {}
+
     @Deprecated("Not supported by scenes") override fun setPulsing(pulsing: Boolean) {}
+
     override fun setQsScrimEnabled(qsScrimEnabled: Boolean) {}
+
     override fun setAmbientIndicationTop(ambientIndicationTop: Int, ambientTextVisible: Boolean) {}
+
     override fun updateSystemUiStateFlags() {}
+
     override fun updateTouchableRegion() {}
+
     override fun transitionToExpandedShade(delay: Long) {}
 
     @Deprecated("Not supported by scenes") override fun resetViewGroupFade() {}
+
     @Deprecated("Not supported by scenes")
     override fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int) {}
+
     @Deprecated("Not supported by scenes") override fun setOverStretchAmount(amount: Float) {}
+
     @Deprecated("TODO(b/325072511) delete this")
     override fun setKeyguardStatusBarAlpha(alpha: Float) {}
+
     override fun showAodUi() {}
+
     @Deprecated(
         "depends on the state you check, use {@link #isShadeFullyExpanded()},\n" +
             "{@link #isOnAod()}, {@link #isOnKeyguard()} instead."
     )
     override val isFullyExpanded = false
+
     override fun handleExternalTouch(event: MotionEvent): Boolean {
         return false
     }
+
     override fun handleExternalInterceptTouch(event: MotionEvent): Boolean {
         return false
     }
 
     override fun startInputFocusTransfer() {}
+
     override fun cancelInputFocusTransfer() {}
+
     override fun finishInputFocusTransfer(velocity: Float) {}
+
+    @Deprecated("Temporary a11y solution until dual shade launch b/371224114")
+    override fun onStatusBarLongPress(event: MotionEvent) {}
+
     override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl()
     override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl()
     @Deprecated("Use SceneInteractor.currentScene instead.")
@@ -98,20 +133,26 @@
 
 class ShadeHeadsUpTrackerEmptyImpl : ShadeHeadsUpTracker {
     override fun addTrackingHeadsUpListener(listener: Consumer<ExpandableNotificationRow>) {}
+
     override fun removeTrackingHeadsUpListener(listener: Consumer<ExpandableNotificationRow>) {}
+
     override fun setHeadsUpAppearanceController(
         headsUpAppearanceController: HeadsUpAppearanceController?
     ) {}
+
     override val trackedHeadsUpNotification: ExpandableNotificationRow? = null
 }
 
 class ShadeFoldAnimatorEmptyImpl : ShadeFoldAnimator {
     override fun prepareFoldToAodAnimation() {}
+
     override fun startFoldToAodAnimation(
         startAction: Runnable,
         endAction: Runnable,
         cancelAction: Runnable,
     ) {}
+
     override fun cancelFoldToAodAnimation() {}
+
     override val view: ViewGroup? = null
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
index 3a483f4..f151307 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
@@ -41,7 +41,6 @@
                 } else {
                     Scenes.Shade
                 }
-            // TODO(b/336581871): add sceneState?
             sceneInteractor.changeScene(key, "animateCollapseQs")
         }
     }
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 5db1dcb..f741b85 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.smartspace.config
 
-import com.android.systemui.Flags.smartspaceSwipeEventLogging
+import com.android.systemui.Flags.smartspaceSwipeEventLoggingFix
 import com.android.systemui.Flags.smartspaceViewpager2
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.plugins.BcSmartspaceConfigPlugin
@@ -30,5 +30,5 @@
         get() = smartspaceViewpager2()
 
     override val isSwipeEventLoggingEnabled: Boolean
-        get() = smartspaceSwipeEventLogging()
+        get() = smartspaceSwipeEventLoggingFix()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 30f564f..3a24ec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -61,15 +61,6 @@
             return row.getEntry().getSbn().getNotification();
         }
     };
-    private static final ResultApplicator GREY_APPLICATOR = new ResultApplicator() {
-        @Override
-        public void apply(View parent, View view, boolean apply, boolean reset) {
-            CachingIconView icon = view.findViewById(com.android.internal.R.id.icon);
-            if (icon != null) {
-                icon.setGrayedOut(apply);
-            }
-        }
-    };
 
     private final ExpandableNotificationRow mRow;
     private final ArrayList<Processor> mProcessors = new ArrayList<>();
@@ -78,14 +69,18 @@
     public NotificationGroupingUtil(ExpandableNotificationRow row) {
         mRow = row;
 
-        final IconComparator iconVisibilityComparator = new IconComparator(mRow) {
+        final IconComparator iconVisibilityComparator = new IconComparator() {
             public boolean compare(View parent, View child, Object parentData,
                     Object childData) {
+                if (Flags.notificationsRedesignAppIcons() && mRow.isShowingAppIcon()) {
+                    // Icon is always the same when we're showing the app icon.
+                    return true;
+                }
                 return hasSameIcon(parentData, childData)
                         && hasSameColor(parentData, childData);
             }
         };
-        final IconComparator greyComparator = new IconComparator(mRow) {
+        final IconComparator greyComparator = new IconComparator() {
             public boolean compare(View parent, View child, Object parentData,
                     Object childData) {
                 if (Flags.notificationsRedesignAppIcons() && mRow.isShowingAppIcon()) {
@@ -95,6 +90,19 @@
                         || hasSameColor(parentData, childData);
             }
         };
+        final ResultApplicator greyApplicator = new ResultApplicator() {
+            @Override
+            public void apply(View parent, View view, boolean apply, boolean reset) {
+                if (Flags.notificationsRedesignAppIcons() && mRow.isShowingAppIcon()) {
+                    // Do nothing.
+                    return;
+                }
+                CachingIconView icon = view.findViewById(com.android.internal.R.id.icon);
+                if (icon != null) {
+                    icon.setGrayedOut(apply);
+                }
+            }
+        };
 
         // To hide the icons if they are the same and the color is the same
         mProcessors.add(new Processor(mRow,
@@ -107,7 +115,7 @@
                 com.android.internal.R.id.status_bar_latest_event_content,
                 ICON_EXTRACTOR,
                 greyComparator,
-                GREY_APPLICATOR));
+                greyApplicator));
         // To show the large icon on the left side instead if all the small icons are the same
         mProcessors.add(new Processor(mRow,
                 com.android.internal.R.id.status_bar_latest_event_content,
@@ -319,13 +327,14 @@
 
     private interface ViewComparator {
         /**
-         * @param parent the view with the given id in the group header
-         * @param child the view with the given id in the child notification
+         * @param parent     the view with the given id in the group header
+         * @param child      the view with the given id in the child notification
          * @param parentData optional data for the parent
-         * @param childData optional data for the child
+         * @param childData  optional data for the child
          * @return whether to views are the same
          */
         boolean compare(View parent, View child, Object parentData, Object childData);
+
         boolean isEmpty(View view);
     }
 
@@ -368,21 +377,12 @@
     }
 
     private abstract static class IconComparator implements ViewComparator {
-        private final ExpandableNotificationRow mRow;
-
-        IconComparator(ExpandableNotificationRow row) {
-            mRow = row;
-        }
-
         @Override
         public boolean compare(View parent, View child, Object parentData, Object childData) {
             return false;
         }
 
         protected boolean hasSameIcon(Object parentData, Object childData) {
-            if (Flags.notificationsRedesignAppIcons() && mRow.isShowingAppIcon()) {
-                return true;
-            }
             Icon parentIcon = getIcon((Notification) parentData);
             Icon childIcon = getIcon((Notification) childData);
             return parentIcon.sameAs(childIcon);
@@ -420,9 +420,9 @@
     private interface ResultApplicator {
         /**
          * @param parent the root view of the child notification
-         * @param view the view with the given id in the child notification
-         * @param apply whether the state should be applied or removed
-         * @param reset if [de]application is the result of a reset
+         * @param view   the view with the given id in the child notification
+         * @param apply  whether the state should be applied or removed
+         * @param reset  if [de]application is the result of a reset
          */
         void apply(View parent, View view, boolean apply, boolean reset);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9b1e782..fdc1c0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -53,6 +53,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
@@ -686,6 +687,7 @@
     }
 
     public void checkRemoteInputOutside(MotionEvent event) {
+        SceneContainerFlag.assertInLegacyMode();
         if (event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
                 && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
                 && isRemoteInputActive()) {
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/core/CommandQueueInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
index 57c8bc6..614f0f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
@@ -46,7 +46,7 @@
 ) : CoreStartable {
 
     override fun start() {
-        StatusBarSimpleFragment.assertInNewMode()
+        StatusBarConnectedDisplays.assertInNewMode()
         val result: RegisterStatusBarResult =
             try {
                 barService.registerStatusBar(commandQueue)
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 72cd63f..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,8 +17,6 @@
 package com.android.systemui.statusbar.dagger;
 
 import com.android.systemui.dagger.SysUISingleton;
-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;
@@ -30,8 +28,7 @@
  * Dagger Module providing {@link CentralSurfacesImpl}.
  */
 @Module(includes = {CentralSurfacesDependenciesModule.class,
-        StatusBarNotificationPresenterModule.class,
-        NotificationsModule.class, NotificationRowModule.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/data/repository/RemoteInputRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
index 9af4b8c..cd1e2ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
@@ -16,16 +16,21 @@
 
 package com.android.systemui.statusbar.data.repository
 
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.NotificationRemoteInputManager
 import com.android.systemui.statusbar.RemoteInputController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
 
 /**
  * Repository used for tracking the state of notification remote input (e.g. when the user presses
@@ -42,30 +47,50 @@
     val remoteInputRowBottomBound: Flow<Float?>
 
     fun setRemoteInputRowBottomBound(bottom: Float?)
+
+    /** Close any active remote inputs */
+    fun closeRemoteInputs()
 }
 
 @SysUISingleton
 class RemoteInputRepositoryImpl
 @Inject
-constructor(private val notificationRemoteInputManager: NotificationRemoteInputManager) :
-    RemoteInputRepository {
-    override val isRemoteInputActive: Flow<Boolean> = conflatedCallbackFlow {
-        trySend(false) // initial value is false
+constructor(
+    @Application applicationScope: CoroutineScope,
+    private val notificationRemoteInputManager: NotificationRemoteInputManager,
+) : RemoteInputRepository {
+    private val _isRemoteInputActive = conflatedCallbackFlow {
         val callback =
             object : RemoteInputController.Callback {
                 override fun onRemoteInputActive(active: Boolean) {
                     trySend(active)
                 }
             }
+        trySend(notificationRemoteInputManager.isRemoteInputActive)
         notificationRemoteInputManager.addControllerCallback(callback)
         awaitClose { notificationRemoteInputManager.removeControllerCallback(callback) }
     }
 
+    override val isRemoteInputActive =
+        if (SceneContainerFlag.isEnabled) {
+            _isRemoteInputActive.stateIn(
+                applicationScope,
+                SharingStarted.WhileSubscribed(),
+                notificationRemoteInputManager.isRemoteInputActive,
+            )
+        } else {
+            _isRemoteInputActive
+        }
+
     override val remoteInputRowBottomBound = MutableStateFlow<Float?>(null)
 
     override fun setRemoteInputRowBottomBound(bottom: Float?) {
         remoteInputRowBottomBound.value = bottom
     }
+
+    override fun closeRemoteInputs() {
+        notificationRemoteInputManager.closeRemoteInputs()
+    }
 }
 
 @Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
index b83b0cc..ea8c3e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
@@ -40,4 +40,9 @@
     fun setRemoteInputRowBottomBound(bottom: Float?) {
         remoteInputRepository.setRemoteInputRowBottomBound(bottom)
     }
+
+    /** Close any active remote inputs */
+    fun closeRemoteInputs() {
+        remoteInputRepository.closeRemoteInputs()
+    }
 }
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/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index ea515e0..08ffbf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -22,11 +22,13 @@
 import androidx.core.animation.ObjectAnimator
 import com.android.app.animation.Interpolators
 import com.android.app.animation.InterpolatorsAndroidX
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Dumpable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.PulseExpansionInteractor
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.shade.ShadeExpansionChangeEvent
 import com.android.systemui.shade.ShadeExpansionListener
@@ -49,7 +51,6 @@
 import kotlin.math.max
 import kotlin.math.min
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 @SysUISingleton
 class NotificationWakeUpCoordinator
@@ -65,6 +66,7 @@
     private val logger: NotificationWakeUpCoordinatorLogger,
     private val notifsKeyguardInteractor: NotificationsKeyguardInteractor,
     private val communalInteractor: CommunalInteractor,
+    private val pulseExpansionInteractor: PulseExpansionInteractor,
 ) :
     OnHeadsUpChangedListener,
     StatusBarStateController.StateListener,
@@ -115,7 +117,7 @@
                     // they were blocked by the proximity sensor
                     updateNotificationVisibility(
                         animate = shouldAnimateVisibility(),
-                        increaseSpeed = false
+                        increaseSpeed = false,
                     )
                 }
             }
@@ -139,7 +141,7 @@
                 // the waking up callback
                 updateNotificationVisibility(
                     animate = shouldAnimateVisibility(),
-                    increaseSpeed = false
+                    increaseSpeed = false,
                 )
             }
         }
@@ -200,7 +202,7 @@
                         setNotificationsVisibleForExpansion(
                             visible = false,
                             animate = false,
-                            increaseSpeed = false
+                            increaseSpeed = false,
                         )
                     }
                 }
@@ -226,7 +228,7 @@
                 for (listener in wakeUpListeners) {
                     listener.onPulseExpandingChanged(pulseExpanding)
                 }
-                notifsKeyguardInteractor.setPulseExpanding(pulseExpanding)
+                pulseExpansionInteractor.setPulseExpanding(pulseExpanding)
             }
         }
     }
@@ -241,7 +243,7 @@
     fun setNotificationsVisibleForExpansion(
         visible: Boolean,
         animate: Boolean,
-        increaseSpeed: Boolean
+        increaseSpeed: Boolean,
     ) {
         notificationsVisibleForExpansion = visible
         updateNotificationVisibility(animate, increaseSpeed)
@@ -282,7 +284,7 @@
     private fun setNotificationsVisible(
         visible: Boolean,
         animate: Boolean,
-        increaseSpeed: Boolean
+        increaseSpeed: Boolean,
     ) {
         if (notificationsVisible == visible) {
             return
@@ -363,7 +365,7 @@
             hardOverride = hardDozeAmountOverride,
             outputLinear = outputLinearDozeAmount,
             state = statusBarStateController.state,
-            changed = changed
+            changed = changed,
         )
         stackScrollerController.setDozeAmount(outputEasedDozeAmount)
         updateHideAmount()
@@ -372,7 +374,7 @@
             setNotificationsVisibleForExpansion(
                 visible = false,
                 animate = false,
-                increaseSpeed = false
+                increaseSpeed = false,
             )
         }
     }
@@ -389,10 +391,7 @@
      * call with `false` at some point in the near future. A call with `true` before that will
      * happen if the animation is not already running.
      */
-    fun setWakingUp(
-        wakingUp: Boolean,
-        requestDelayedAnimation: Boolean,
-    ) {
+    fun setWakingUp(wakingUp: Boolean, requestDelayedAnimation: Boolean) {
         logger.logSetWakingUp(wakingUp, requestDelayedAnimation)
         this.wakingUp = wakingUp
         if (wakingUp && requestDelayedAnimation) {
@@ -432,7 +431,7 @@
             // See: UnlockedScreenOffAnimationController.onFinishedWakingUp()
             setHardDozeAmountOverride(
                 dozing = false,
-                source = "Override: Shade->Shade (lock cancelled by unlock)"
+                source = "Override: Shade->Shade (lock cancelled by unlock)",
             )
             this.state = newState
             return
@@ -478,7 +477,7 @@
                 wasCollapsedEnoughToHide,
                 isCollapsedEnoughToHide,
                 couldShowPulsingHuns,
-                canShowPulsingHuns
+                canShowPulsingHuns,
             )
 
             if (couldShowPulsingHuns && !canShowPulsingHuns) {
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/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/data/repository/NotificationsKeyguardViewStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
index bd6ea30..f9fd5ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepository.kt
@@ -24,7 +24,4 @@
 class NotificationsKeyguardViewStateRepository @Inject constructor() {
     /** Are notifications fully hidden from view? */
     val areNotificationsFullyHidden = MutableStateFlow(false)
-
-    /** Is a pulse expansion occurring? */
-    val isPulseExpanding = MutableStateFlow(false)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
index a6361cb..1cb4144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractor.kt
@@ -22,12 +22,7 @@
 /** Domain logic pertaining to notifications on the keyguard. */
 class NotificationsKeyguardInteractor
 @Inject
-constructor(
-    private val repository: NotificationsKeyguardViewStateRepository,
-) {
-    /** Is a pulse expansion occurring? */
-    val isPulseExpanding: Flow<Boolean> = repository.isPulseExpanding
-
+constructor(private val repository: NotificationsKeyguardViewStateRepository) {
     /** Are notifications fully hidden from view? */
     val areNotificationsFullyHidden: Flow<Boolean> = repository.areNotificationsFullyHidden
 
@@ -35,9 +30,4 @@
     fun setNotificationsFullyHidden(fullyHidden: Boolean) {
         repository.areNotificationsFullyHidden.value = fullyHidden
     }
-
-    /** Updates whether a pulse expansion is occurring. */
-    fun setPulseExpanding(pulseExpanding: Boolean) {
-        repository.isPulseExpanding.value = pulseExpanding
-    }
 }
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 bf30322..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
@@ -40,10 +40,10 @@
 import androidx.annotation.NonNull;
 
 import com.android.settingslib.Utils;
-import com.android.systemui.Flags;
 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;
@@ -107,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.
@@ -187,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
@@ -196,6 +217,7 @@
     }
 
     private void updateManageOrHistoryButtonText() {
+        NotifRedesignFooter.assertInLegacyMode();
         if (mManageOrHistoryButtonTextId == 0) {
             return; // not initialized yet
         }
@@ -204,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;
         }
@@ -215,6 +238,7 @@
     }
 
     private void updateManageOrHistoryButtonDescription() {
+        NotifRedesignFooter.assertInLegacyMode();
         if (mManageOrHistoryButtonDescriptionId == 0) {
             return; // not initialized yet
         }
@@ -273,7 +297,7 @@
         }
         super.onFinishInflate();
         mClearAllButton = (FooterViewButton) findSecondaryView();
-        if (Flags.notificationsRedesignFooterView()) {
+        if (NotifRedesignFooter.isEnabled()) {
             mSettingsButton = findViewById(R.id.settings_button);
             mHistoryButton = findViewById(R.id.history_button);
         } else {
@@ -311,11 +335,17 @@
 
     /** 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);
     }
 
@@ -324,6 +354,7 @@
      * in the redesign.
      */
     public void setManageButtonClickListener(OnClickListener listener) {
+        NotifRedesignFooter.assertInLegacyMode();
         mManageOrHistoryButton.setOnClickListener(listener);
     }
 
@@ -364,7 +395,7 @@
             updateClearAllButtonText();
             updateClearAllButtonDescription();
 
-            if (!Flags.notificationsRedesignFooterView()) {
+            if (!NotifRedesignFooter.isEnabled()) {
                 updateManageOrHistoryButtonText();
                 updateManageOrHistoryButtonDescription();
             }
@@ -444,7 +475,7 @@
         }
         mClearAllButton.setBackground(clearAllBg);
         mClearAllButton.setTextColor(onSurface);
-        if (Flags.notificationsRedesignFooterView()) {
+        if (NotifRedesignFooter.isEnabled()) {
             mSettingsButton.setBackground(manageBg);
             mSettingsButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
 
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 34894a2..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
@@ -20,11 +20,11 @@
 import androidx.lifecycle.lifecycleScope
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.Flags
 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
@@ -66,7 +66,7 @@
         notificationActivityStarter: NotificationActivityStarter,
     ) = coroutineScope {
         launch { bindClearAllButton(footer, viewModel, clearAllNotifications) }
-        if (!Flags.notificationsRedesignFooterView()) {
+        if (!NotifRedesignFooter.isEnabled) {
             launch {
                 bindManageOrHistoryButton(
                     footer,
@@ -77,8 +77,8 @@
                 )
             }
         } else {
-            bindSettingsButtonListener(footer, notificationActivityStarter)
-            bindHistoryButtonListener(footer, notificationActivityStarter)
+            launch { bindSettingsButton(footer, viewModel, notificationActivityStarter) }
+            launch { bindHistoryButton(footer, viewModel, notificationActivityStarter) }
         }
         launch { bindMessage(footer, viewModel) }
     }
@@ -122,10 +122,11 @@
         }
     }
 
-    private fun bindSettingsButtonListener(
+    private suspend fun bindSettingsButton(
         footer: FooterView,
+        viewModel: FooterViewModel,
         notificationActivityStarter: NotificationActivityStarter,
-    ) {
+    ) = coroutineScope {
         val settingsIntent =
             SettingsIntent.forNotificationSettings(
                 cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
@@ -134,12 +135,20 @@
             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 fun bindHistoryButtonListener(
+    private suspend fun bindHistoryButton(
         footer: FooterView,
+        viewModel: FooterViewModel,
         notificationActivityStarter: NotificationActivityStarter,
-    ) {
+    ) = coroutineScope {
         val settingsIntent =
             SettingsIntent.forNotificationHistory(
                 cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
@@ -148,6 +157,13 @@
             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(
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 d8021fa..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,6 +95,10 @@
                     .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
 
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..08d177f 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;
@@ -1988,6 +1987,7 @@
         mColorUpdateLogger = colorUpdateLogger;
         mDismissibilityProvider = dismissibilityProvider;
         mFeatureFlags = featureFlags;
+        setHapticFeedbackEnabled(!com.android.systemui.Flags.msdlFeedback());
     }
 
     private void initDimens() {
@@ -2948,18 +2948,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/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 23a2fac..baad616 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -64,6 +64,9 @@
 import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.wmshell.BubblesManager;
 
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
 import java.util.List;
 import java.util.Optional;
 
@@ -114,6 +117,7 @@
     private final NotificationDismissibilityProvider mDismissibilityProvider;
     private final IStatusBarService mStatusBarService;
     private final UiEventLogger mUiEventLogger;
+    private final MSDLPlayer mMSDLPlayer;
 
     private final NotificationSettingsController mSettingsController;
 
@@ -273,7 +277,8 @@
             ExpandableNotificationRowDragController dragController,
             NotificationDismissibilityProvider dismissibilityProvider,
             IStatusBarService statusBarService,
-            UiEventLogger uiEventLogger) {
+            UiEventLogger uiEventLogger,
+            MSDLPlayer msdlPlayer) {
         mView = view;
         mListContainer = listContainer;
         mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory;
@@ -309,6 +314,7 @@
         mDismissibilityProvider = dismissibilityProvider;
         mStatusBarService = statusBarService;
         mUiEventLogger = uiEventLogger;
+        mMSDLPlayer = msdlPlayer;
     }
 
     /**
@@ -352,6 +358,9 @@
             }
 
             mView.setLongPressListener((v, x, y, item) -> {
+                if (com.android.systemui.Flags.msdlFeedback()) {
+                    mMSDLPlayer.playToken(MSDLToken.LONG_PRESS, null);
+                }
                 if (mView.isSummaryWithChildren()) {
                     mView.expandNotification();
                     return true;
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 c1d72e4..57af8ea 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
@@ -678,7 +678,9 @@
         mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
         mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
-        setWindowInsetsAnimationCallback(mInsetsCallback);
+        if (!SceneContainerFlag.isEnabled()) {
+            setWindowInsetsAnimationCallback(mInsetsCallback);
+        }
     }
 
     /**
@@ -795,7 +797,7 @@
                 && !onKeyguard()
                 && mUpcomingStatusBarState != StatusBarState.KEYGUARD
                 // quick settings don't affect notifications when not in full screen
-                && (mQsExpansionFraction != 1 || !mQsFullScreen)
+                && (getQsExpansionFraction() != 1 || !mQsFullScreen)
                 && !mScreenOffAnimationController.shouldHideNotificationsFooter()
                 && !mIsRemoteInputActive;
     }
@@ -1526,7 +1528,7 @@
         float fraction = mAmbientState.getExpansionFraction();
         // If we are on quick settings, we need to quickly hide it to show the bouncer to avoid an
         // overlap. Otherwise, we maintain the normal fraction for smoothness.
-        if (mAmbientState.isBouncerInTransit() && mQsExpansionFraction > 0f) {
+        if (mAmbientState.isBouncerInTransit() && getQsExpansionFraction() > 0f) {
             fraction = BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(fraction);
         }
         final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
@@ -1550,7 +1552,7 @@
             }
             updateInterpolatedStackHeight(endHeight, fraction);
         } else {
-            if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) {
+            if (getQsExpansionFraction() <= 0 && !shouldSkipHeightUpdate()) {
                 final float endHeight = updateStackEndHeight(
                         getHeight(), getEmptyBottomMarginInternal(), getTopPadding());
                 updateInterpolatedStackHeight(endHeight, fraction);
@@ -1694,7 +1696,7 @@
             } else if (mQsFullScreen) {
                 int stackStartPosition =
                         getContentHeight() - getTopPadding() + getIntrinsicPadding();
-                int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
+                int stackEndPosition = getMaxTopPadding() + mShelf.getIntrinsicHeight();
                 if (stackStartPosition <= stackEndPosition) {
                     stackHeight = stackEndPosition;
                 } else {
@@ -1703,7 +1705,7 @@
                         stackHeight = (int) height;
                     } else {
                         stackHeight = (int) NotificationUtils.interpolate(stackStartPosition,
-                                stackEndPosition, mQsExpansionFraction);
+                                stackEndPosition, getQsExpansionFraction());
                     }
                 }
             } else {
@@ -2086,6 +2088,7 @@
     }
 
     private void updateImeInset(WindowInsets windowInsets) {
+        SceneContainerFlag.assertInLegacyMode();
         mImeInset = windowInsets.getInsets(WindowInsets.Type.ime()).bottom;
 
         if (mFooterView != null && mFooterView.getViewState() != null) {
@@ -2112,7 +2115,7 @@
         if (cutout != null) {
             mWaterfallTopInset = cutout.getWaterfallInsets().top;
         }
-        if (!mIsInsetAnimationRunning) {
+        if (!SceneContainerFlag.isEnabled() && !mIsInsetAnimationRunning) {
             // update bottom inset e.g. after rotation
             updateImeInset(insets);
         }
@@ -2513,6 +2516,7 @@
     }
 
     private int getImeInset() {
+        SceneContainerFlag.assertInLegacyMode();
         // The NotificationStackScrollLayout does not extend all the way to the bottom of the
         // display. Therefore, subtract that space from the mImeInset, in order to only include
         // the portion of the bottom inset that actually overlaps the NotificationStackScrollLayout.
@@ -2851,11 +2855,6 @@
         setExpandedHeight(mExpandedHeight);
     }
 
-    public void setMaxTopPadding(int maxTopPadding) {
-        SceneContainerFlag.assertInLegacyMode();
-        mMaxTopPadding = maxTopPadding;
-    }
-
     public int getLayoutMinHeight() {
         SceneContainerFlag.assertInLegacyMode();
         return getLayoutMinHeightInternal();
@@ -3639,7 +3638,11 @@
      * @return Whether a y coordinate is inside the content.
      */
     public boolean isInContentBounds(float y) {
-        return y < getHeight() - getEmptyBottomMarginInternal();
+        if (SceneContainerFlag.isEnabled()) {
+            return y < mAmbientState.getStackCutoff();
+        } else {
+            return y < getHeight() - getEmptyBottomMarginInternal();
+        }
     }
 
     private float getTouchSlop(MotionEvent event) {
@@ -5268,17 +5271,22 @@
         return mQsFullScreen;
     }
 
+    private float getQsExpansionFraction() {
+        SceneContainerFlag.assertInLegacyMode();
+        return mQsExpansionFraction;
+    }
+
     public void setQsExpansionFraction(float qsExpansionFraction) {
         SceneContainerFlag.assertInLegacyMode();
-        boolean footerAffected = mQsExpansionFraction != qsExpansionFraction
-                && (mQsExpansionFraction == 1 || qsExpansionFraction == 1);
+        boolean footerAffected = getQsExpansionFraction() != qsExpansionFraction
+                && (getQsExpansionFraction() == 1 || qsExpansionFraction == 1);
         mQsExpansionFraction = qsExpansionFraction;
         updateUseRoundedRectClipping();
 
         // If notifications are scrolled,
         // clear out scrollY by the time we push notifications offscreen
         if (getOwnScrollY() > 0) {
-            setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, mQsExpansionFraction));
+            setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, getQsExpansionFraction()));
         }
         if (!FooterViewRefactor.isEnabled() && footerAffected) {
             updateFooter();
@@ -5510,9 +5518,7 @@
             println(pw, "alpha", getAlpha());
             println(pw, "suppressChildrenMeasureLayout", mSuppressChildrenMeasureAndLayout);
             println(pw, "scrollY", mAmbientState.getScrollY());
-            println(pw, "maxTopPadding", mMaxTopPadding);
             println(pw, "showShelfOnly", mShouldShowShelfOnly);
-            println(pw, "qsExpandFraction", mQsExpansionFraction);
             println(pw, "isCurrentUserSetup", mIsCurrentUserSetup);
             println(pw, "hideAmount", mAmbientState.getHideAmount());
             println(pw, "ambientStateSwipingUp", mAmbientState.isSwipingUp());
@@ -5547,6 +5553,8 @@
                 println(pw, "intrinsicContentHeight", getIntrinsicContentHeight());
                 println(pw, "contentHeight", getContentHeight());
                 println(pw, "topPadding", getTopPadding());
+                println(pw, "maxTopPadding", getMaxTopPadding());
+                println(pw, "qsExpandFraction", getQsExpansionFraction());
             }
         });
         pw.println();
@@ -5619,7 +5627,9 @@
                     pw.println("mIsCurrentUserSetup: " + mIsCurrentUserSetup);
                     pw.println("onKeyguard: " + onKeyguard());
                     pw.println("mUpcomingStatusBarState: " + mUpcomingStatusBarState);
-                    pw.println("mQsExpansionFraction: " + mQsExpansionFraction);
+                    if (!SceneContainerFlag.isEnabled()) {
+                        pw.println("QsExpansionFraction: " + getQsExpansionFraction());
+                    }
                     pw.println("mQsFullScreen: " + mQsFullScreen);
                     pw.println(
                             "mScreenOffAnimationController"
@@ -6243,7 +6253,8 @@
         if (SceneContainerFlag.isEnabled()) return;
         // We don't want to clip notifications when QS is expanded, because incoming heads up on
         // the bottom would be clipped otherwise
-        boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
+        boolean qsAllowsClipping =
+                getQsExpansionFraction() < 0.5f || mShouldUseSplitNotificationShade;
         boolean clip = mIsExpanded && qsAllowsClipping;
         if (clip != mShouldUseRoundedRectClipping) {
             mShouldUseRoundedRectClipping = clip;
@@ -6953,10 +6964,12 @@
 
     /** Use {@link ScrollViewFields#intrinsicStackHeight}, when SceneContainerFlag is enabled. */
     private int getContentHeight() {
+        SceneContainerFlag.assertInLegacyMode();
         return mContentHeight;
     }
 
     private void setContentHeight(int contentHeight) {
+        SceneContainerFlag.assertInLegacyMode();
         mContentHeight = contentHeight;
     }
 
@@ -6965,10 +6978,23 @@
      * @return the height of the content ignoring the footer.
      */
     public float getIntrinsicContentHeight() {
+        SceneContainerFlag.assertInLegacyMode();
         return mIntrinsicContentHeight;
     }
 
     private void setIntrinsicContentHeight(float intrinsicContentHeight) {
+        SceneContainerFlag.assertInLegacyMode();
         mIntrinsicContentHeight = intrinsicContentHeight;
     }
+
+    private int getMaxTopPadding() {
+        SceneContainerFlag.assertInLegacyMode();
+        return mMaxTopPadding;
+    }
+
+    /** Not used with SceneContainerFlag, because we rely on the placeholder for placement. */
+    public void setMaxTopPadding(int maxTopPadding) {
+        SceneContainerFlag.assertInLegacyMode();
+        mMaxTopPadding = maxTopPadding;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
index ffab9ea..42acd7bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/SharedNotificationContainer.kt
@@ -29,19 +29,15 @@
 import androidx.constraintlayout.widget.ConstraintSet.TOP
 import androidx.constraintlayout.widget.ConstraintSet.VERTICAL
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition
 
 /**
  * Container for the stack scroller, so that the bounds can be externally specified, such as from
  * the keyguard or shade scenes.
  */
-class SharedNotificationContainer(
-    context: Context,
-    private val attrs: AttributeSet?,
-) :
-    ConstraintLayout(
-        context,
-        attrs,
-    ) {
+class SharedNotificationContainer(context: Context, attrs: AttributeSet?) :
+    ConstraintLayout(context, attrs) {
 
     private val baseConstraintSet = ConstraintSet()
 
@@ -59,24 +55,40 @@
     }
 
     fun updateConstraints(
-        useSplitShade: Boolean,
+        horizontalPosition: HorizontalPosition,
         marginStart: Int,
         marginTop: Int,
         marginEnd: Int,
-        marginBottom: Int
+        marginBottom: Int,
     ) {
         val constraintSet = ConstraintSet()
         constraintSet.clone(baseConstraintSet)
 
         val startConstraintId =
-            if (useSplitShade) {
+            if (horizontalPosition is HorizontalPosition.MiddleToEdge) {
                 R.id.nssl_guideline
             } else {
                 PARENT_ID
             }
+
         val nsslId = R.id.notification_stack_scroller
         constraintSet.apply {
-            connect(nsslId, START, startConstraintId, START, marginStart)
+            if (SceneContainerFlag.isEnabled) {
+                when (horizontalPosition) {
+                    is HorizontalPosition.FloatAtEnd ->
+                        constrainWidth(nsslId, horizontalPosition.width)
+                    is HorizontalPosition.MiddleToEdge ->
+                        setGuidelinePercent(R.id.nssl_guideline, horizontalPosition.ratio)
+                    else -> Unit
+                }
+            }
+
+            if (
+                !SceneContainerFlag.isEnabled ||
+                    horizontalPosition !is HorizontalPosition.FloatAtEnd
+            ) {
+                connect(nsslId, START, startConstraintId, START, marginStart)
+            }
             connect(nsslId, END, PARENT_ID, END, marginEnd)
             connect(nsslId, BOTTOM, PARENT_ID, BOTTOM, marginBottom)
             connect(nsslId, TOP, PARENT_ID, TOP, marginTop)
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 fd19f1f..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
@@ -22,7 +22,6 @@
 import com.android.app.tracing.TraceUtils.traceAsync
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.nano.MetricsProto
-import com.android.systemui.Flags
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
 import com.android.systemui.dagger.qualifiers.Background
@@ -40,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
@@ -146,7 +146,7 @@
             // The footer needs to be re-inflated every time the theme or the font size changes.
             configuration
                 .inflateLayout<FooterView>(
-                    if (Flags.notificationsRedesignFooterView())
+                    if (NotifRedesignFooter.isEnabled)
                         R.layout.status_bar_notification_footer_redesign
                     else R.layout.status_bar_notification_footer,
                     parentView,
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 ce89d78..4a55dfa 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,6 +18,7 @@
 
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Flags
 import com.android.systemui.common.ui.view.onLayoutChanged
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -36,11 +37,8 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Binds the shared notification container to its view-model. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class SharedNotificationContainerBinder
 @Inject
@@ -74,7 +72,7 @@
                     launch {
                         viewModel.configurationBasedDimensions.collect {
                             view.updateConstraints(
-                                useSplitShade = it.useSplitShade,
+                                horizontalPosition = it.horizontalPosition,
                                 marginStart = it.marginStart,
                                 marginTop = it.marginTop,
                                 marginEnd = it.marginEnd,
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 e6663d5..827e2bf 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
@@ -224,33 +224,56 @@
         if (SceneContainerFlag.isEnabled) {
                 combine(
                     shadeInteractor.isShadeLayoutWide,
+                    shadeInteractor.shadeMode,
                     configurationInteractor.onAnyConfigurationChange,
-                ) { isShadeLayoutWide, _ ->
+                ) { isShadeLayoutWide, shadeMode, _ ->
                     with(context.resources) {
-                        // TODO(b/338033836): Define separate horizontal margins for dual shade.
                         val marginHorizontal =
-                            getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)
+                            getDimensionPixelSize(
+                                if (shadeMode is Dual) {
+                                    R.dimen.shade_panel_margin_horizontal
+                                } else {
+                                    R.dimen.notification_panel_margin_horizontal
+                                }
+                            )
+
+                        val horizontalPosition =
+                            when (shadeMode) {
+                                Single -> HorizontalPosition.EdgeToEdge
+                                Split -> HorizontalPosition.MiddleToEdge(ratio = 0.5f)
+                                Dual ->
+                                    if (isShadeLayoutWide) {
+                                        HorizontalPosition.FloatAtEnd(
+                                            width = getDimensionPixelSize(R.dimen.shade_panel_width)
+                                        )
+                                    } else {
+                                        HorizontalPosition.EdgeToEdge
+                                    }
+                            }
+
                         ConfigurationBasedDimensions(
-                            marginStart = if (isShadeLayoutWide) 0 else marginHorizontal,
+                            horizontalPosition = horizontalPosition,
+                            marginStart = if (shadeMode is Split) 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,
                         )
                     }
                 }
             } else {
                 interactor.configurationBasedDimensions.map {
                     ConfigurationBasedDimensions(
+                        horizontalPosition =
+                            if (it.useSplitShade) HorizontalPosition.MiddleToEdge()
+                            else HorizontalPosition.EdgeToEdge,
                         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,
                     )
                 }
             }
@@ -446,59 +469,63 @@
      */
     private val alphaForShadeAndQsExpansion: Flow<Float> =
         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)
+                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 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 (shadeExpansion > 0f || qsExpansion > 0f) {
+                            } 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)
                             }
                         }
-                    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
 
@@ -766,10 +793,25 @@
     }
 
     data class ConfigurationBasedDimensions(
+        val horizontalPosition: HorizontalPosition,
         val marginStart: Int,
         val marginTop: Int,
         val marginEnd: Int,
         val marginBottom: Int,
-        val useSplitShade: Boolean,
     )
+
+    /** Specifies the horizontal layout constraints for the notification container. */
+    sealed interface HorizontalPosition {
+        /** The container is using the full width of the screen (minus any margins). */
+        data object EdgeToEdge : HorizontalPosition
+
+        /** The container is laid out from the given [ratio] of the screen width to the end edge. */
+        data class MiddleToEdge(val ratio: Float = 0.5f) : HorizontalPosition
+
+        /**
+         * The container has a fixed [width] and is aligned to the end of the screen. In this
+         * layout, the start edge of the container is floating, i.e. unconstrained.
+         */
+        data class FloatAtEnd(val width: Int) : HorizontalPosition
+    }
 }
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 65663fd..99efba4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1105,7 +1105,7 @@
         mJavaAdapter.alwaysCollectFlow(
                 mCommunalInteractor.isIdleOnCommunal(),
                 mIdleOnCommunalConsumer);
-        if (SceneContainerFlag.isEnabled()) {
+        if (SceneContainerFlag.isEnabled() || QSComposeFragment.isEnabled()) {
             mJavaAdapter.alwaysCollectFlow(
                     mBrightnessMirrorShowingInteractor.isShowing(),
                     this::setBrightnessMirrorShowing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 98869be..4915b84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -45,6 +45,8 @@
 import com.android.systemui.doze.DozeScreenState;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
@@ -85,6 +87,7 @@
     private final BatteryController mBatteryController;
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private final DozeInteractor mDozeInteractor;
+    private final KeyguardTransitionInteractor mTransitionInteractor;
     private final FoldAodAnimationController mFoldAodAnimationController;
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     private final UserTracker mUserTracker;
@@ -134,6 +137,7 @@
             StatusBarStateController statusBarStateController,
             UserTracker userTracker,
             DozeInteractor dozeInteractor,
+            KeyguardTransitionInteractor transitionInteractor,
             SecureSettings secureSettings) {
         mResources = resources;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
@@ -148,6 +152,7 @@
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mUserTracker = userTracker;
         mDozeInteractor = dozeInteractor;
+        mTransitionInteractor = transitionInteractor;
         mSecureSettings = secureSettings;
 
         keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
@@ -353,6 +358,9 @@
      * delayed for a few seconds. This might be useful to play animations without reducing FPS.
      */
     public boolean shouldDelayDisplayDozeTransition() {
+        if (mTransitionInteractor.getTransitionState().getValue().getTo() == KeyguardState.AOD) {
+            return true;
+        }
         return willAnimateFromLockScreenToAod()
                 || mScreenOffAnimationController.shouldDelayDisplayDozeTransition();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index e7d9717..91c43dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -39,6 +39,8 @@
 import com.android.systemui.Flags;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.res.R;
+import com.android.systemui.shade.LongPressGestureDetector;
+import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
@@ -67,6 +69,7 @@
     private InsetsFetcher mInsetsFetcher;
     private int mDensity;
     private float mFontScale;
+    private LongPressGestureDetector mLongPressGestureDetector;
 
     /**
      * Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -78,6 +81,12 @@
         mStatusBarWindowControllerStore = Dependency.get(StatusBarWindowControllerStore.class);
     }
 
+    void setLongPressGestureDetector(LongPressGestureDetector longPressGestureDetector) {
+        if (ShadeExpandsOnStatusBarLongPress.isEnabled()) {
+            mLongPressGestureDetector = longPressGestureDetector;
+        }
+    }
+
     void setTouchEventHandler(Gefingerpoken handler) {
         mTouchEventHandler = handler;
     }
@@ -198,6 +207,9 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (ShadeExpandsOnStatusBarLongPress.isEnabled() && mLongPressGestureDetector != null) {
+            mLongPressGestureDetector.handleTouch(event);
+        }
         if (mTouchEventHandler == null) {
             Log.w(
                     TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 746d6a7..c24f432 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -33,7 +33,9 @@
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.shade.LongPressGestureDetector
 import com.android.systemui.shade.ShadeController
+import com.android.systemui.shade.ShadeExpandsOnStatusBarLongPress
 import com.android.systemui.shade.ShadeLogger
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
@@ -66,6 +68,7 @@
     private val shadeController: ShadeController,
     private val shadeViewController: ShadeViewController,
     private val panelExpansionInteractor: PanelExpansionInteractor,
+    private val longPressGestureDetector: Provider<LongPressGestureDetector>,
     private val windowRootView: Provider<WindowRootView>,
     private val shadeLogger: ShadeLogger,
     private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
@@ -114,6 +117,10 @@
         addDarkReceivers()
         addCursorSupportToIconContainers()
 
+        if (ShadeExpandsOnStatusBarLongPress.isEnabled) {
+            mView.setLongPressGestureDetector(longPressGestureDetector.get())
+        }
+
         progressProvider?.setReadyToHandleTransition(true)
         configurationController.addCallback(configurationListener)
 
@@ -328,6 +335,7 @@
         private val shadeController: ShadeController,
         private val shadeViewController: ShadeViewController,
         private val panelExpansionInteractor: PanelExpansionInteractor,
+        private val longPressGestureDetector: Provider<LongPressGestureDetector>,
         private val windowRootView: Provider<WindowRootView>,
         private val shadeLogger: ShadeLogger,
         private val viewUtil: ViewUtil,
@@ -352,6 +360,7 @@
                 shadeController,
                 shadeViewController,
                 panelExpansionInteractor,
+                longPressGestureDetector,
                 windowRootView,
                 shadeLogger,
                 statusBarMoveFromCenterAnimationController,
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..0c511aea 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;
@@ -1338,6 +1348,7 @@
         if (!canHandleBackPressed()) {
             return;
         }
+        mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
 
         boolean hideBouncerOverDream = isBouncerShowing()
                 && mDreamOverlayStateController.isOverlayActive();
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/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index a658115..d2c2003 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -37,6 +37,7 @@
 import com.android.systemui.ScreenDecorations;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
@@ -78,6 +79,7 @@
     private View mNotificationShadeWindowView;
     private View mNotificationPanelView;
     private boolean mForceCollapsedUntilLayout = false;
+    private Boolean mCommunalVisible = false;
 
     private Region mTouchableRegion = new Region();
     private int mDisplayCutoutTouchableRegionSize;
@@ -98,7 +100,8 @@
             JavaAdapter javaAdapter,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             PrimaryBouncerInteractor primaryBouncerInteractor,
-            AlternateBouncerInteractor alternateBouncerInteractor
+            AlternateBouncerInteractor alternateBouncerInteractor,
+            CommunalSceneInteractor communalSceneInteractor
     ) {
         mContext = context;
         initResources();
@@ -145,6 +148,9 @@
             javaAdapter.alwaysCollectFlow(
                     shadeInteractor.isAnyExpanded(),
                     this::onShadeOrQsExpanded);
+            javaAdapter.alwaysCollectFlow(
+                    communalSceneInteractor.isCommunalVisible(),
+                    this::onCommunalVisible);
         }
 
         mPrimaryBouncerInteractor = primaryBouncerInteractor;
@@ -196,6 +202,10 @@
         }
     }
 
+    private void onCommunalVisible(Boolean visible) {
+        mCommunalVisible = visible;
+    }
+
     /**
      * Calculates the touch region needed for heads up notifications, taking into consideration
      * any existing display cutouts (notch)
@@ -304,6 +314,9 @@
                 && (!mIsSceneContainerUiEmpty || mIsRemoteUserInteractionOngoing))
                 || mPrimaryBouncerInteractor.isShowing().getValue()
                 || mAlternateBouncerInteractor.isVisibleState()
+                // The glanceable hub is a full-screen UI within the notification shade window. When
+                // it's visible, the touchable region should be the full screen.
+                || mCommunalVisible
                 || mUnlockedScreenOffAnimationController.isAnimationPlaying();
     }
 
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..23f3482 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
@@ -140,7 +143,7 @@
         fun commandQueueInitializerCoreStartable(
             initializerLazy: Lazy<CommandQueueInitializer>
         ): CoreStartable {
-            return if (StatusBarSimpleFragment.isEnabled) {
+            return if (StatusBarConnectedDisplays.isEnabled) {
                 initializerLazy.get()
             } else {
                 CoreStartable.NOP
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/statusbar/policy/ui/dialog/composable/ModeTile.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
index 76389f3..b033b36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
@@ -46,7 +46,7 @@
 import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModeTileViewModel
 
 @Composable
-fun ModeTile(viewModel: ModeTileViewModel) {
+fun ModeTile(viewModel: ModeTileViewModel, modifier: Modifier = Modifier) {
     val tileColor: Color by
         animateColorAsState(
             if (viewModel.enabled) MaterialTheme.colorScheme.primary
@@ -59,7 +59,7 @@
         )
 
     CompositionLocalProvider(LocalContentColor provides contentColor) {
-        Surface(color = tileColor, shape = RoundedCornerShape(16.dp)) {
+        Surface(color = tileColor, shape = RoundedCornerShape(16.dp), modifier = modifier) {
             Row(
                 modifier =
                     Modifier.combinedClickable(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt
index 5392e38..16f24f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTileGrid.kt
@@ -17,29 +17,74 @@
 package com.android.systemui.statusbar.policy.ui.dialog.composable
 
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.grid.GridCells
 import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.Flags
+import com.android.systemui.qs.panels.ui.compose.PagerDots
 import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
 
 @Composable
 fun ModeTileGrid(viewModel: ModesDialogViewModel) {
     val tiles by viewModel.tiles.collectAsStateWithLifecycle(initialValue = emptyList())
 
-    LazyVerticalGrid(
-        columns = GridCells.Fixed(1),
-        modifier = Modifier.fillMaxWidth().heightIn(max = 320.dp),
-        verticalArrangement = Arrangement.spacedBy(8.dp),
-        horizontalArrangement = Arrangement.spacedBy(8.dp),
-    ) {
-        items(tiles.size, key = { index -> tiles[index].id }) { index ->
-            ModeTile(viewModel = tiles[index])
+    if (Flags.modesUiDialogPaging()) {
+        val tilesPerPage = 3
+        val totalPages = { (tiles.size + tilesPerPage - 1) / tilesPerPage }
+        val pagerState = rememberPagerState(initialPage = 0, pageCount = totalPages)
+
+        Column {
+            HorizontalPager(
+                state = pagerState,
+                modifier = Modifier.fillMaxWidth(),
+                pageSpacing = 16.dp,
+                verticalAlignment = Alignment.Top,
+                // Pre-emptively layout and compose the next page, to make sure the height stays
+                // the same even if we have fewer than [tilesPerPage] tiles on the last page.
+                beyondViewportPageCount = 1,
+            ) { page ->
+                Column(
+                    modifier = Modifier.fillMaxWidth().fillMaxHeight(),
+                    verticalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.Top),
+                ) {
+                    val startIndex = page * tilesPerPage
+                    val endIndex = minOf((page + 1) * tilesPerPage, tiles.size)
+                    for (index in startIndex until endIndex) {
+                        ModeTile(viewModel = tiles[index], modifier = Modifier.fillMaxWidth())
+                    }
+                }
+            }
+
+            PagerDots(
+                pagerState = pagerState,
+                activeColor = MaterialTheme.colorScheme.primary,
+                nonActiveColor = MaterialTheme.colorScheme.onSurfaceVariant,
+                modifier = Modifier.align(Alignment.CenterHorizontally).padding(top = 8.dp),
+            )
+        }
+    } else {
+        LazyVerticalGrid(
+            columns = GridCells.Fixed(1),
+            modifier = Modifier.fillMaxWidth().heightIn(max = 280.dp),
+            verticalArrangement = Arrangement.spacedBy(8.dp),
+            horizontalArrangement = Arrangement.spacedBy(8.dp),
+        ) {
+            items(tiles.size, key = { index -> tiles[index].id }) { index ->
+                ModeTile(viewModel = tiles[index])
+            }
         }
     }
 }
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/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
index d2ed71c..55cdfb2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
@@ -19,11 +19,8 @@
 import android.content.Context;
 import android.os.Handler;
 
-import com.android.systemui.Flags;
 import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
 
-import dagger.Lazy;
 import dagger.assisted.Assisted;
 import dagger.assisted.AssistedFactory;
 import dagger.assisted.AssistedInject;
@@ -40,13 +37,11 @@
     private final WakeLock mInner;
 
     @AssistedInject
-    public DelayedWakeLock(@Background Lazy<Handler> bgHandler,
-                           @Main Lazy<Handler> mainHandler,
+    public DelayedWakeLock(@Background Handler bgHandler,
                            Context context, WakeLockLogger logger,
             @Assisted String tag) {
         mInner = WakeLock.createPartial(context, logger, tag);
-        mHandler = Flags.delayedWakelockReleaseOnBackgroundThread() ? bgHandler.get()
-                : mainHandler.get();
+        mHandler = bgHandler;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
index f763ee4..f00e3d1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java
@@ -18,14 +18,9 @@
 
 import android.content.Context;
 import android.os.PowerManager;
-import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.Flags;
-
-import java.util.HashMap;
-
 import javax.inject.Inject;
 
 /** WakeLock wrapper for testability */
@@ -114,59 +109,7 @@
     @VisibleForTesting
     static WakeLock wrap(
             final PowerManager.WakeLock inner, WakeLockLogger logger, long maxTimeout) {
-        if (Flags.delayedWakelockReleaseOnBackgroundThread()) {
-            return new ClientTrackingWakeLock(inner, logger, maxTimeout);
-        }
-
-        // Non-thread safe implementation, remove when flag above is removed.
-        return new WakeLock() {
-            private final HashMap<String, Integer> mActiveClients = new HashMap<>();
-
-            /** @see PowerManager.WakeLock#acquire() */
-            public void acquire(String why) {
-                mActiveClients.putIfAbsent(why, 0);
-                int count = mActiveClients.get(why) + 1;
-                mActiveClients.put(why, count);
-                if (logger != null) {
-                    logger.logAcquire(inner, why, count);
-                }
-                if (maxTimeout == Builder.NO_TIMEOUT) {
-                    inner.acquire();
-                } else {
-                    inner.acquire(maxTimeout);
-                }
-            }
-
-            /** @see PowerManager.WakeLock#release() */
-            public void release(String why) {
-                Integer count = mActiveClients.get(why);
-                if (count == null) {
-                    Log.wtf(TAG, "Releasing WakeLock with invalid reason: " + why,
-                            new Throwable());
-                    return;
-                }
-                count--;
-                if (count == 0) {
-                    mActiveClients.remove(why);
-                } else {
-                    mActiveClients.put(why, count);
-                }
-                if (logger != null) {
-                    logger.logRelease(inner, why, count);
-                }
-                inner.release();
-            }
-
-            /** @see PowerManager.WakeLock#wrap(Runnable) */
-            public Runnable wrap(Runnable runnable) {
-                return wrapImpl(this, runnable);
-            }
-
-            @Override
-            public String toString() {
-                return "active clients= " + mActiveClients;
-            }
-        };
+        return new ClientTrackingWakeLock(inner, logger, maxTimeout);
     }
 
     /**
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/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index fb108c5..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>(extraBufferCapacity = 1)
-    val dialogVisibility: Flow<VolumeDialogVisibilityModel> = repository.dialogVisibility
+    val dialogVisibility: Flow<VolumeDialogVisibilityModel> =
+        repository.dialogVisibility
+            .onEach { controller.notifyVisible(it is Visible) }
+            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+            .filterNotNull()
 
     init {
         merge(
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
index c05a0b2..6816d35 100644
--- 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
@@ -16,15 +16,26 @@
 
 package com.android.systemui.volume.dialog.ringer.ui.binder
 
+import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+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.ringer.ui.viewmodel.RingerButtonViewModel
+import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerDrawerState
+import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerViewModel
+import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerViewModelState
 import com.android.systemui.volume.dialog.ringer.ui.viewmodel.VolumeDialogRingerDrawerViewModel
 import javax.inject.Inject
-import kotlinx.coroutines.awaitCancellation
+import kotlin.math.abs
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 
 @VolumeDialogScope
 class VolumeDialogRingerViewBinder
@@ -33,16 +44,124 @@
 
     fun bind(view: View) {
         with(view) {
+            val drawerAndRingerContainer =
+                requireViewById<View>(R.id.volume_ringer_and_drawer_container)
+            val drawerContainer = requireViewById<View>(R.id.volume_drawer_container)
+            val selectedButtonView =
+                requireViewById<ImageButton>(R.id.volume_new_ringer_active_button)
+            val volumeDialogView = requireViewById<ViewGroup>(R.id.volume_dialog)
             repeatWhenAttached {
                 viewModel(
                     traceName = "VolumeDialogRingerViewBinder",
                     minWindowLifecycleState = WindowLifecycleState.ATTACHED,
                     factory = { viewModelFactory.create() },
                 ) { viewModel ->
-                    setSnapshotBinding {}
-                    awaitCancellation()
+                    viewModel.ringerViewModel
+                        .onEach { ringerState ->
+                            when (ringerState) {
+                                is RingerViewModelState.Available -> {
+                                    val uiModel = ringerState.uiModel
+
+                                    bindSelectedButton(viewModel, uiModel, selectedButtonView)
+                                    bindDrawerButtons(viewModel, uiModel.availableButtons)
+
+                                    // Set up views background and visibility
+                                    drawerAndRingerContainer.visibility = View.VISIBLE
+                                    when (uiModel.drawerState) {
+                                        is RingerDrawerState.Initial -> {
+                                            drawerContainer.visibility = View.GONE
+                                            selectedButtonView.visibility = View.VISIBLE
+                                            volumeDialogView.setBackgroundResource(
+                                                R.drawable.volume_dialog_background
+                                            )
+                                        }
+
+                                        is RingerDrawerState.Closed -> {
+                                            drawerContainer.visibility = View.GONE
+                                            selectedButtonView.visibility = View.VISIBLE
+                                            volumeDialogView.setBackgroundResource(
+                                                R.drawable.volume_dialog_background
+                                            )
+                                        }
+
+                                        is RingerDrawerState.Open -> {
+                                            drawerContainer.visibility = View.VISIBLE
+                                            selectedButtonView.visibility = View.GONE
+                                            if (
+                                                uiModel.currentButtonIndex !=
+                                                    uiModel.availableButtons.size - 1
+                                            ) {
+                                                volumeDialogView.setBackgroundResource(
+                                                    R.drawable.volume_dialog_background_small_radius
+                                                )
+                                            }
+                                        }
+                                    }
+                                }
+
+                                is RingerViewModelState.Unavailable -> {
+                                    drawerAndRingerContainer.visibility = View.GONE
+                                    volumeDialogView.setBackgroundResource(
+                                        R.drawable.volume_dialog_background
+                                    )
+                                }
+                            }
+                        }
+                        .launchIn(this)
                 }
             }
         }
     }
+
+    private fun View.bindDrawerButtons(
+        viewModel: VolumeDialogRingerDrawerViewModel,
+        availableButtons: List<RingerButtonViewModel?>,
+    ) {
+        val drawerOptions = requireViewById<ViewGroup>(R.id.volume_drawer_options)
+        val count = availableButtons.size
+        drawerOptions.ensureChildCount(R.layout.volume_ringer_button, count)
+
+        availableButtons.fastForEachIndexed { index, ringerButton ->
+            ringerButton?.let {
+                drawerOptions.getChildAt(count - index - 1).bindDrawerButton(it, viewModel)
+            }
+        }
+    }
+
+    private fun View.bindDrawerButton(
+        buttonViewModel: RingerButtonViewModel,
+        viewModel: VolumeDialogRingerDrawerViewModel,
+    ) {
+        with(requireViewById<ImageButton>(R.id.volume_drawer_button)) {
+            setImageResource(buttonViewModel.imageResId)
+            contentDescription = context.getString(buttonViewModel.contentDescriptionResId)
+            setOnClickListener { viewModel.onRingerButtonClicked(buttonViewModel.ringerMode) }
+        }
+    }
+
+    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) }
+            }
+        }
+    }
+
+    private fun bindSelectedButton(
+        viewModel: VolumeDialogRingerDrawerViewModel,
+        uiModel: RingerViewModel,
+        selectedButtonView: ImageButton,
+    ) {
+        with(uiModel) {
+            selectedButtonView.setImageResource(selectedButton.imageResId)
+            selectedButtonView.setOnClickListener {
+                viewModel.onRingerButtonClicked(selectedButton.ringerMode)
+            }
+        }
+    }
 }
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
index a09bfeb..96d4f62 100644
--- 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
@@ -22,6 +22,8 @@
     val availableButtons: List<RingerButtonViewModel?>,
     /** The index of the currently selected button */
     val currentButtonIndex: Int,
+    /** Currently selected button. */
+    val selectedButton: RingerButtonViewModel,
     /** For open and close animations */
     val drawerState: RingerDrawerState,
 )
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
index 5b73107..d4da226 100644
--- 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
@@ -41,8 +41,6 @@
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.stateIn
 
-private const val TAG = "VolumeDialogRingerDrawerViewModel"
-
 class VolumeDialogRingerDrawerViewModel
 @AssistedInject
 constructor(
@@ -111,31 +109,45 @@
         return if (currentIndex == -1 || isSingleVolume) {
             RingerViewModelState.Unavailable
         } else {
-            RingerViewModelState.Available(
-                RingerViewModel(
-                    availableButtons = availableModes.map { mode -> toButtonViewModel(mode) },
-                    currentButtonIndex = currentIndex,
-                    drawerState = drawerState,
+            toButtonViewModel(currentRingerMode, isSelectedButton = true)?.let {
+                RingerViewModelState.Available(
+                    RingerViewModel(
+                        availableButtons = availableModes.map { mode -> toButtonViewModel(mode) },
+                        currentButtonIndex = currentIndex,
+                        selectedButton = it,
+                        drawerState = drawerState,
+                    )
                 )
-            )
+            } ?: RingerViewModelState.Unavailable
         }
     }
 
     private fun VolumeDialogRingerModel.toButtonViewModel(
-        ringerMode: RingerMode
+        ringerMode: RingerMode,
+        isSelectedButton: Boolean = false,
     ): RingerButtonViewModel? {
         return when (ringerMode.value) {
             RINGER_MODE_SILENT ->
                 RingerButtonViewModel(
                     imageResId = R.drawable.ic_speaker_mute,
-                    contentDescriptionResId = R.string.volume_ringer_status_silent,
+                    contentDescriptionResId =
+                        if (isSelectedButton) {
+                            R.string.volume_ringer_status_silent
+                        } else {
+                            R.string.volume_ringer_hint_mute
+                        },
                     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,
+                    contentDescriptionResId =
+                        if (isSelectedButton) {
+                            R.string.volume_ringer_status_vibrate
+                        } else {
+                            R.string.volume_ringer_hint_vibrate
+                        },
                     hintLabelResId = R.string.volume_ringer_hint_vibrate,
                     ringerMode = ringerMode,
                 )
@@ -143,8 +155,18 @@
                 when {
                     isMuted && isEnabled ->
                         RingerButtonViewModel(
-                            imageResId = R.drawable.ic_speaker_mute,
-                            contentDescriptionResId = R.string.volume_ringer_status_normal,
+                            imageResId =
+                                if (isSelectedButton) {
+                                    R.drawable.ic_speaker_mute
+                                } else {
+                                    R.drawable.ic_speaker_on
+                                },
+                            contentDescriptionResId =
+                                if (isSelectedButton) {
+                                    R.string.volume_ringer_status_normal
+                                } else {
+                                    R.string.volume_ringer_hint_unmute
+                                },
                             hintLabelResId = R.string.volume_ringer_hint_unmute,
                             ringerMode = ringerMode,
                         )
@@ -152,7 +174,12 @@
                     availableModes.contains(RingerMode(RINGER_MODE_VIBRATE)) ->
                         RingerButtonViewModel(
                             imageResId = R.drawable.ic_speaker_on,
-                            contentDescriptionResId = R.string.volume_ringer_status_normal,
+                            contentDescriptionResId =
+                                if (isSelectedButton) {
+                                    R.string.volume_ringer_status_normal
+                                } else {
+                                    R.string.volume_ringer_hint_unmute
+                                },
                             hintLabelResId = R.string.volume_ringer_hint_vibrate,
                             ringerMode = ringerMode,
                         )
@@ -160,7 +187,12 @@
                     else ->
                         RingerButtonViewModel(
                             imageResId = R.drawable.ic_speaker_on,
-                            contentDescriptionResId = R.string.volume_ringer_status_normal,
+                            contentDescriptionResId =
+                                if (isSelectedButton) {
+                                    R.string.volume_ringer_status_normal
+                                } else {
+                                    R.string.volume_ringer_hint_unmute
+                                },
                             hintLabelResId = R.string.volume_ringer_hint_mute,
                             ringerMode = ringerMode,
                         )
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/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/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 5c4d53a..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
@@ -73,6 +73,7 @@
     }
 
     private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) {
+        slider.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> }
         with(slider) {
             valueFrom = levelMin.toFloat()
             valueTo = levelMax.toFloat()
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 f486fe1..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
@@ -23,14 +23,14 @@
 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 kotlin.math.abs
-import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 
 @VolumeDialogScope
 class VolumeDialogSlidersViewBinder
@@ -48,20 +48,20 @@
                     minWindowLifecycleState = WindowLifecycleState.ATTACHED,
                     factory = { viewModelFactory.create() },
                 ) { viewModel ->
-                    setSnapshotBinding {
-                        viewModel.uiModel?.sliderViewBinder?.bind(volumeDialog)
+                    viewModel.sliders
+                        .onEach { uiModel ->
+                            uiModel.sliderViewBinder.bind(volumeDialog)
 
-                        val floatingSliderViewBinders =
-                            viewModel.uiModel?.floatingSliderViewBinders ?: emptyList()
-                        floatingSlidersContainer.ensureChildCount(
-                            viewLayoutId = R.layout.volume_dialog_slider_floating,
-                            count = floatingSliderViewBinders.size,
-                        )
-                        floatingSliderViewBinders.fastForEachIndexed { index, viewBinder ->
-                            viewBinder.bind(floatingSlidersContainer.getChildAt(index))
+                            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))
+                            }
                         }
-                    }
-                    awaitCancellation()
+                        .launchIn(this)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index 22cf89f..b5b292f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -16,9 +16,6 @@
 
 package com.android.systemui.volume.dialog.sliders.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.VolumeDialog
 import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
 import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
@@ -27,9 +24,10 @@
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -41,10 +39,9 @@
     private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
     private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory,
     private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory,
-) : ExclusiveActivatable() {
+) {
 
-    private val hydrator = Hydrator("VolumeDialogSlidersViewModel")
-    private val slidersStateFlow: StateFlow<VolumeDialogSliderUiModel?> =
+    val sliders: Flow<VolumeDialogSliderUiModel> =
         slidersInteractor.sliders
             .distinctUntilChanged()
             .map { slidersModel ->
@@ -55,13 +52,7 @@
                 )
             }
             .stateIn(coroutineScope, SharingStarted.Eagerly, null)
-
-    val uiModel: VolumeDialogSliderUiModel? by
-        hydrator.hydratedStateOf("VolumeDialogSlidersViewModel#uiModel", slidersStateFlow)
-
-    override suspend fun onActivated(): Nothing {
-        hydrator.activate()
-    }
+            .filterNotNull()
 
     private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder =
         sliderViewBinderFactory.create {
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 cd535e4..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
+++ /dev/null
@@ -1,93 +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.View
-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.ringer.ui.binder.VolumeDialogRingerViewBinder
-import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
-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 slidersViewBinder: VolumeDialogSlidersViewBinder,
-    private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder,
-    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)
-
-            with(dialog.requireViewById<View>(R.id.volume_dialog_container)) {
-                volumeDialogRingerViewBinder.bind(this)
-                slidersViewBinder.bind(this)
-                settingsButtonViewBinder.bind(this)
-                volumeDialogViewBinder.bind(dialog, this)
-            }
-        }
-    }
-
-    /** 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..cec3d1e 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
@@ -22,12 +22,15 @@
 import android.media.AudioManager.STREAM_MUSIC
 import android.media.AudioManager.STREAM_NOTIFICATION
 import android.util.Log
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.settingslib.volume.shared.model.AudioStreamModel
 import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.Flags
 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
@@ -48,7 +51,6 @@
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Models a particular slider state. */
 class AudioStreamSliderViewModel
@@ -61,6 +63,7 @@
     private val zenModeInteractor: ZenModeInteractor,
     private val uiEventLogger: UiEventLogger,
     private val volumePanelLogger: VolumePanelLogger,
+    private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
 ) : SliderViewModel {
 
     private val volumeChanges = MutableStateFlow<Int?>(null)
@@ -169,6 +172,13 @@
         }
     }
 
+    override fun getSliderHapticsViewModelFactory(): SliderHapticsViewModel.Factory? =
+        if (Flags.hapticsForComposeSliders() && slider.value != SliderState.Empty) {
+            hapticsViewModelFactory
+        } else {
+            null
+        }
+
     private fun AudioStreamModel.toState(
         isEnabled: Boolean,
         ringerMode: RingerMode,
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..0d80452 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
@@ -18,7 +18,10 @@
 
 import android.content.Context
 import android.media.session.MediaController.PlaybackInfo
+import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags
 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
@@ -31,7 +34,6 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 class CastVolumeSliderViewModel
 @AssistedInject
@@ -40,6 +42,7 @@
     @Assisted private val coroutineScope: CoroutineScope,
     private val context: Context,
     private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+    private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
 ) : SliderViewModel {
 
     override val slider: StateFlow<SliderState> =
@@ -60,6 +63,13 @@
         // do nothing because this action isn't supported for Cast sliders.
     }
 
+    override fun getSliderHapticsViewModelFactory(): SliderHapticsViewModel.Factory? =
+        if (Flags.hapticsForComposeSliders() && slider.value != SliderState.Empty) {
+            hapticsViewModelFactory
+        } else {
+            null
+        }
+
     private fun PlaybackInfo.getCurrentState(): State {
         val volumeRange = 0..maxVolume
         return State(
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..4d9552f 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. */
@@ -28,4 +29,6 @@
     fun onValueChangeFinished()
 
     fun toggleMuted(state: SliderState)
+
+    fun getSliderHapticsViewModelFactory(): SliderHapticsViewModel.Factory?
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index b941fde..3d9eb53 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -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/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 61eeab3..e1b8a1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -22,7 +22,6 @@
 import android.hardware.biometrics.BiometricAuthenticator
 import android.hardware.biometrics.BiometricConstants
 import android.hardware.biometrics.BiometricManager
-import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
 import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
 import android.hardware.biometrics.PromptInfo
 import android.hardware.biometrics.PromptVerticalListContentView
@@ -138,6 +137,7 @@
         PromptSelectorInteractorImpl(
             fingerprintRepository,
             displayStateInteractor,
+            credentialInteractor,
             biometricPromptRepository,
             lockPatternUtils,
         )
@@ -412,7 +412,6 @@
 
     @Test
     fun testShowBiometricUI_ContentViewWithMoreOptionsButton() {
-        mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         var isButtonClicked = false
         val contentView =
             PromptContentViewWithMoreOptionsButton.Builder()
@@ -449,7 +448,6 @@
 
     @Test
     fun testShowCredentialUI_withVerticalListContentView() {
-        mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         val container =
             initializeFingerprintContainer(
                 authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
@@ -470,7 +468,6 @@
 
     @Test
     fun testShowCredentialUI_withContentViewWithMoreOptionsButton() {
-        mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         val contentView =
             PromptContentViewWithMoreOptionsButton.Builder()
                 .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
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/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/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..6069b44 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,8 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
+    @DisableSceneContainer
     public void testUpdateFooter_remoteInput() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -625,7 +627,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 +643,8 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
+    @DisableSceneContainer
     public void testUpdateFooter_oneClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -657,7 +660,8 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
+    @DisableSceneContainer
     public void testUpdateFooter_withoutHistory() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -674,7 +678,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 +694,8 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
+    @DisableSceneContainer
     public void testUpdateFooter_oneNonClearableNotification() {
         setBarStateForTest(StatusBarState.SHADE);
         mStackScroller.setCurrentUserSetup(true);
@@ -1181,7 +1186,7 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
+    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
     public void hasFilteredOutSeenNotifs_updateFooter() {
         mStackScroller.setCurrentUserSetup(true);
 
@@ -1206,6 +1211,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void testWindowInsetAnimationProgress_updatesBottomInset() {
         int imeInset = 100;
         WindowInsets windowInsets = new WindowInsets.Builder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 008e8ce..638f195 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -40,6 +40,7 @@
 import com.android.systemui.plugins.fakeDarkIconDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.shade.LongPressGestureDetector
 import com.android.systemui.shade.ShadeControllerImpl
 import com.android.systemui.shade.ShadeLogger
 import com.android.systemui.shade.ShadeViewController
@@ -97,6 +98,7 @@
     @Mock private lateinit var windowRootView: Provider<WindowRootView>
     @Mock private lateinit var shadeLogger: ShadeLogger
     @Mock private lateinit var viewUtil: ViewUtil
+    @Mock private lateinit var longPressGestureDetector: LongPressGestureDetector
     private lateinit var statusBarWindowStateController: StatusBarWindowStateController
 
     private lateinit var view: PhoneStatusBarView
@@ -393,6 +395,7 @@
                 shadeControllerImpl,
                 shadeViewController,
                 panelExpansionInteractor,
+                { longPressGestureDetector },
                 windowRootView,
                 shadeLogger,
                 viewUtil,
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/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt
new file mode 100644
index 0000000..0c0b5ba
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/data/repository/KeyguardBypassRepositoryTest.kt
@@ -0,0 +1,238 @@
+/*
+ * 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.phone.data.repository
+
+import android.provider.Settings
+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.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.KeyguardBypassRepository
+import com.android.systemui.keyguard.data.repository.configureKeyguardBypass
+import com.android.systemui.keyguard.data.repository.keyguardBypassRepository
+import com.android.systemui.keyguard.data.repository.verifyCallback
+import com.android.systemui.keyguard.data.repository.verifyNoCallback
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
+import com.android.systemui.statusbar.policy.devicePostureController
+import com.android.systemui.testKosmos
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.tuner.tunerService
+import com.android.systemui.util.mockito.withArgCaptor
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
+class KeyguardBypassRepositoryTest : SysuiTestCase() {
+    @JvmField @Rule val mockito: MockitoRule = MockitoJUnit.rule()
+
+    private lateinit var tunableCallback: TunerService.Tunable
+    private lateinit var postureControllerCallback: DevicePostureController.Callback
+
+    private val kosmos = testKosmos()
+    private lateinit var underTest: KeyguardBypassRepository
+    private val testScope = kosmos.testScope
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    // overrideFaceBypassSetting overridden to true
+    // isFaceEnrolledAndEnabled true
+    // isPostureAllowedForFaceAuth true/false on posture changes
+    @Test
+    fun updatesBypassAvailableOnPostureChanges_bypassOverrideAlways() =
+        testScope.runTest {
+            // KeyguardBypassRepository#overrideFaceBypassSetting = true due to ALWAYS override
+            // Initialize face auth posture to DEVICE_POSTURE_OPENED config
+            initUnderTest(
+                faceUnlockBypassOverrideConfig = FACE_UNLOCK_BYPASS_ALWAYS,
+                faceAuthPostureConfig = DEVICE_POSTURE_CLOSED,
+            )
+            val isBypassAvailable by collectLastValue(underTest.isBypassAvailable)
+            runCurrent()
+
+            postureControllerCallback = kosmos.devicePostureController.verifyCallback()
+
+            // Update face auth posture to match config
+            postureControllerCallback.onPostureChanged(DEVICE_POSTURE_CLOSED)
+
+            // Assert bypass available
+            assertThat(isBypassAvailable).isTrue()
+
+            // Set face auth posture to not match config
+            postureControllerCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+            // Assert bypass not available
+            assertThat(isBypassAvailable).isFalse()
+        }
+
+    // overrideFaceBypassSetting overridden to false
+    // isFaceEnrolledAndEnabled true
+    // isPostureAllowedForFaceAuth true/false on posture changes
+    @Test
+    fun updatesBypassEnabledOnPostureChanges_bypassOverrideNever() =
+        testScope.runTest {
+            // KeyguardBypassRepository#overrideFaceBypassSetting = false due to NEVER override
+            // Initialize face auth posture to DEVICE_POSTURE_OPENED config
+            initUnderTest(
+                faceUnlockBypassOverrideConfig = FACE_UNLOCK_BYPASS_NEVER,
+                faceAuthPostureConfig = DEVICE_POSTURE_CLOSED,
+            )
+            val bypassEnabled by collectLastValue(underTest.isBypassAvailable)
+            runCurrent()
+            postureControllerCallback = kosmos.devicePostureController.verifyCallback()
+
+            // Update face auth posture to match config
+            postureControllerCallback.onPostureChanged(DEVICE_POSTURE_CLOSED)
+
+            // Assert bypass not enabled
+            assertThat(bypassEnabled).isFalse()
+
+            // Set face auth posture to not match config
+            postureControllerCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+            // Assert bypass not enabled
+            assertThat(bypassEnabled).isFalse()
+        }
+
+    // overrideFaceBypassSetting set true/false depending on Setting
+    // isFaceEnrolledAndEnabled true
+    // isPostureAllowedForFaceAuth true
+    @Test
+    fun updatesBypassEnabledOnSettingsChanges_bypassNoOverride_devicePostureMatchesConfig() =
+        testScope.runTest {
+            // No bypass override
+            // Initialize face auth posture to DEVICE_POSTURE_OPENED config
+            initUnderTest(
+                faceUnlockBypassOverrideConfig = FACE_UNLOCK_BYPASS_NO_OVERRIDE,
+                faceAuthPostureConfig = DEVICE_POSTURE_CLOSED,
+            )
+
+            val bypassEnabled by collectLastValue(underTest.isBypassAvailable)
+            runCurrent()
+            postureControllerCallback = kosmos.devicePostureController.verifyCallback()
+            tunableCallback = kosmos.tunerService.captureCallback()
+
+            // Update face auth posture to match config
+            postureControllerCallback.onPostureChanged(DEVICE_POSTURE_CLOSED)
+
+            // FACE_UNLOCK_DISMISSES_KEYGUARD setting true
+            whenever(kosmos.tunerService.getValue(eq(faceUnlockDismissesKeyguard), anyInt()))
+                .thenReturn(1)
+            tunableCallback.onTuningChanged(faceUnlockDismissesKeyguard, "")
+
+            runCurrent()
+            // Assert bypass enabled
+            assertThat(bypassEnabled).isTrue()
+
+            // FACE_UNLOCK_DISMISSES_KEYGUARD setting false
+            whenever(kosmos.tunerService.getValue(eq(faceUnlockDismissesKeyguard), anyInt()))
+                .thenReturn(0)
+            tunableCallback.onTuningChanged(faceUnlockDismissesKeyguard, "")
+
+            runCurrent()
+            // Assert bypass not enabled
+            assertThat(bypassEnabled).isFalse()
+        }
+
+    // overrideFaceBypassSetting overridden to true
+    // isFaceEnrolledAndEnabled true
+    // isPostureAllowedForFaceAuth always true given DEVICE_POSTURE_UNKNOWN config
+    @Test
+    fun bypassEnabledTrue_bypassAlways_unknownDevicePostureConfig() =
+        testScope.runTest {
+            // KeyguardBypassRepository#overrideFaceBypassSetting = true due to ALWAYS override
+            // Set face auth posture config to unknown
+            initUnderTest(
+                faceUnlockBypassOverrideConfig = FACE_UNLOCK_BYPASS_ALWAYS,
+                faceAuthPostureConfig = DEVICE_POSTURE_UNKNOWN,
+            )
+            val bypassEnabled by collectLastValue(underTest.isBypassAvailable)
+            kosmos.devicePostureController.verifyNoCallback()
+
+            // Assert bypass enabled
+            assertThat(bypassEnabled).isTrue()
+        }
+
+    // overrideFaceBypassSetting overridden to false
+    // isFaceEnrolledAndEnabled true
+    // isPostureAllowedForFaceAuth always true given DEVICE_POSTURE_UNKNOWN config
+    @Test
+    fun bypassEnabledFalse_bypassNever_unknownDevicePostureConfig() =
+        testScope.runTest {
+            // KeyguardBypassRepository#overrideFaceBypassSetting = false due to NEVER override
+            // Set face auth posture config to unknown
+            initUnderTest(
+                faceUnlockBypassOverrideConfig = FACE_UNLOCK_BYPASS_NEVER,
+                faceAuthPostureConfig = DEVICE_POSTURE_UNKNOWN,
+            )
+            val bypassEnabled by collectLastValue(underTest.isBypassAvailable)
+            kosmos.devicePostureController.verifyNoCallback()
+
+            // Assert bypass enabled
+            assertThat(bypassEnabled).isFalse()
+        }
+
+    private fun TestScope.initUnderTest(
+        faceUnlockBypassOverrideConfig: Int,
+        faceAuthPostureConfig: Int,
+    ) {
+        kosmos.configureKeyguardBypass(
+            faceAuthEnrolledAndEnabled = true,
+            faceUnlockBypassOverrideConfig = faceUnlockBypassOverrideConfig,
+            faceAuthPostureConfig = faceAuthPostureConfig,
+        )
+        underTest = kosmos.keyguardBypassRepository
+        runCurrent()
+    }
+
+    companion object {
+        private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
+        private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
+        private const val FACE_UNLOCK_BYPASS_NEVER = 2
+    }
+}
+
+private const val faceUnlockDismissesKeyguard = Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD
+
+private fun TunerService.captureCallback() =
+    withArgCaptor<TunerService.Tunable> {
+        verify(this@captureCallback).addTunable(capture(), eq(faceUnlockDismissesKeyguard))
+    }
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..48106de 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,57 @@
                 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);
+    }
+
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void testEventLogging_bubbleBar_expandAndCollapse() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        mEntryListener.onEntryAdded(mRow);
+        mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow.getKey(), 0);
+
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
+
+        mBubbleController.collapseStack();
+
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_COLLAPSED));
+    }
+
+    @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+    @Test
+    public void testEventLogging_bubbleBar_autoExpandingBubble() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        setMetadataFlags(mRow,
+                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
+        mEntryListener.onEntryAdded(mRow);
+
+        verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+                eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED));
+    }
+
     /** 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/SysUITestModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
index 020f7fa..c945812 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
@@ -30,6 +30,8 @@
 import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.data.repository.PulseExpansionRepository
 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
 import com.android.systemui.scene.SceneContainerFrameworkModule
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -70,11 +72,17 @@
 interface SysUITestModule {
 
     @Binds fun bindTestableContext(sysuiTestableContext: SysuiTestableContext): TestableContext
+
     @Binds fun bindContext(testableContext: TestableContext): Context
+
     @Binds @Application fun bindAppContext(context: Context): Context
+
     @Binds @Application fun bindAppResources(resources: Resources): Resources
+
     @Binds @Main fun bindMainResources(resources: Resources): Resources
+
     @Binds fun bindBroadcastDispatcher(fake: FakeBroadcastDispatcher): BroadcastDispatcher
+
     @Binds @SysUISingleton fun bindsShadeInteractor(sii: ShadeInteractorImpl): ShadeInteractor
 
     @Binds
@@ -108,7 +116,7 @@
         @Provides
         fun provideBaseShadeInteractor(
             sceneContainerOn: Provider<ShadeInteractorSceneContainerImpl>,
-            sceneContainerOff: Provider<ShadeInteractorLegacyImpl>
+            sceneContainerOff: Provider<ShadeInteractorLegacyImpl>,
         ): BaseShadeInteractor {
             return if (SceneContainerFlag.isEnabled) {
                 sceneContainerOn.get()
@@ -125,6 +133,12 @@
         ): SceneDataSourceDelegator {
             return SceneDataSourceDelegator(applicationScope, config)
         }
+
+        @Provides
+        @SysUISingleton
+        fun providesPulseExpansionRepository(dumpManager: DumpManager): PulseExpansionRepository {
+            return PulseExpansionRepository(dumpManager)
+        }
     }
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt
index 56297f0..787a471 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt
@@ -27,6 +27,7 @@
         fingerprintPropertyRepository = fingerprintPropertyRepository,
         displayStateInteractor = displayStateInteractor,
         promptRepository = promptRepository,
-        lockPatternUtils = lockPatternUtils
+        credentialInteractor = FakeCredentialInteractor(),
+        lockPatternUtils = lockPatternUtils,
     )
 }
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/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 62d221d..b27dadc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -40,8 +40,18 @@
     ) {
         coroutineScope.launch {
             val id = nextWidgetId++
-            val providerInfo = AppWidgetProviderInfo().apply { this.provider = provider }
-            val configured = configurator?.configureWidget(id) ?: true
+            val providerInfo = createAppWidgetProviderInfo(provider, user.identifier)
+
+            fakeDatabase[id] =
+                CommunalWidgetContentModel.Available(
+                    appWidgetId = id,
+                    rank = rank ?: 0,
+                    providerInfo = providerInfo,
+                    spanY = 3,
+                )
+            updateListFromDatabase()
+
+            val configured = configurator?.configureWidget(id) != false
             if (configured) {
                 onConfigured(id, providerInfo, rank ?: -1)
             }
@@ -61,20 +71,15 @@
                 appWidgetId = appWidgetId,
                 rank = rank,
                 providerInfo =
-                    AppWidgetProviderInfo().apply {
-                        provider = ComponentName.unflattenFromString(componentName)!!
-                        widgetCategory = category
-                        providerInfo =
-                            ActivityInfo().apply {
-                                applicationInfo =
-                                    ApplicationInfo().apply {
-                                        uid = userId * UserHandle.PER_USER_RANGE
-                                    }
-                            }
-                    },
+                    createAppWidgetProviderInfo(
+                        ComponentName.unflattenFromString(componentName)!!,
+                        userId,
+                        category,
+                    ),
                 spanY = spanY,
             )
         updateListFromDatabase()
+        nextWidgetId = appWidgetId + 1
     }
 
     fun addPendingWidget(
@@ -151,4 +156,20 @@
             )
         updateListFromDatabase()
     }
+
+    private fun createAppWidgetProviderInfo(
+        componentName: ComponentName,
+        userId: Int,
+        category: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+    ): AppWidgetProviderInfo {
+        return AppWidgetProviderInfo().apply {
+            provider = componentName
+            widgetCategory = category
+            providerInfo =
+                ActivityInfo().apply {
+                    applicationInfo =
+                        ApplicationInfo().apply { uid = userId * UserHandle.PER_USER_RANGE }
+                }
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 629fda6..1f68195 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -54,7 +54,6 @@
         keyguardInteractor = keyguardInteractor,
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         communalSettingsInteractor = communalSettingsInteractor,
-        appWidgetHost = mock(),
         editWidgetsActivityStarter = editWidgetsActivityStarter,
         userTracker = userTracker,
         activityStarter = activityStarter,
@@ -62,7 +61,7 @@
         sceneInteractor = sceneInteractor,
         logBuffer = logcatLogBuffer("CommunalInteractor"),
         tableLogBuffer = mock(),
-        managedProfileController = fakeManagedProfileController
+        managedProfileController = fakeManagedProfileController,
     )
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/FakeGlanceableHubMultiUserHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/FakeGlanceableHubMultiUserHelper.kt
new file mode 100644
index 0000000..de44399
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/FakeGlanceableHubMultiUserHelper.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.communal.shared.model
+
+class FakeGlanceableHubMultiUserHelper(
+    override val glanceableHubHsumFlagEnabled: Boolean = true,
+    private var isHeadlessSystemUserMode: Boolean = false,
+    private var isInHeadlessSystemUser: Boolean = false,
+) : GlanceableHubMultiUserHelper {
+
+    override fun isHeadlessSystemUserMode(): Boolean {
+        return isHeadlessSystemUserMode
+    }
+
+    fun setIsHeadlessSystemUserMode(isHeadlessSystemUserMode: Boolean) {
+        this.isHeadlessSystemUserMode = isHeadlessSystemUserMode
+    }
+
+    override fun isInHeadlessSystemUser(): Boolean {
+        return isInHeadlessSystemUser
+    }
+
+    fun setIsInHeadlessSystemUser(isInHeadlessSystemUser: Boolean) {
+        this.isInHeadlessSystemUser = isInHeadlessSystemUser
+    }
+
+    override fun assertInHeadlessSystemUser() {
+        check(isInHeadlessSystemUser())
+    }
+
+    override fun assertNotInHeadlessSystemUser() {
+        check(!isInHeadlessSystemUser())
+    }
+}
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/communal/shared/model/GlanceableHubMultiUserHelperKosmos.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/communal/shared/model/GlanceableHubMultiUserHelperKosmos.kt
index 06592b1..adee440 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/model/GlanceableHubMultiUserHelperKosmos.kt
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.communal.shared.model
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
+val Kosmos.fakeGlanceableHubMultiUserHelper by Kosmos.Fixture { FakeGlanceableHubMultiUserHelper() }
+
+val Kosmos.glanceableHubMultiUserHelper by
+    Kosmos.Fixture<GlanceableHubMultiUserHelper> { fakeGlanceableHubMultiUserHelper }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerKosmos.kt
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerKosmos.kt
index 83cbc52..583ae44 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/widgets/GlanceableHubWidgetManagerKosmos.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.communal.widgets
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.mockGlanceableHubWidgetManager by Kosmos.Fixture<GlanceableHubWidgetManager> { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
index 878e385..2a7e3e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorKosmos.kt
@@ -20,6 +20,7 @@
 
 import com.android.keyguard.logging.biometricUnlockLogger
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.dump.dumpManager
 import com.android.systemui.keyevent.domain.interactor.keyEventInteractor
 import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.kosmos.Kosmos
@@ -27,17 +28,19 @@
 import com.android.systemui.util.time.systemClock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
+@OptIn(ExperimentalCoroutinesApi::class)
 val Kosmos.deviceEntryHapticsInteractor by
     Kosmos.Fixture {
         DeviceEntryHapticsInteractor(
-            deviceEntrySourceInteractor = deviceEntrySourceInteractor,
-            deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
-            deviceEntryBiometricAuthInteractor = deviceEntryBiometricAuthInteractor,
-            fingerprintPropertyRepository = fingerprintPropertyRepository,
             biometricSettingsRepository = biometricSettingsRepository,
+            deviceEntryBiometricAuthInteractor = deviceEntryBiometricAuthInteractor,
+            deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
+            deviceEntrySourceInteractor = deviceEntrySourceInteractor,
+            fingerprintPropertyRepository = fingerprintPropertyRepository,
             keyEventInteractor = keyEventInteractor,
+            logger = biometricUnlockLogger,
             powerInteractor = powerInteractor,
             systemClock = systemClock,
-            logger = biometricUnlockLogger,
+            dumpManager = dumpManager,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorKosmos.kt
index 0b9ec92..f91a044 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntrySourceInteractorKosmos.kt
@@ -16,14 +16,34 @@
 
 package com.android.systemui.deviceentry.domain.interactor
 
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.biometrics.authController
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.keyguard.domain.interactor.keyguardBypassInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.statusbar.phone.dozeScrimController
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 @ExperimentalCoroutinesApi
 val Kosmos.deviceEntrySourceInteractor by
     Kosmos.Fixture {
         DeviceEntrySourceInteractor(
+            authenticationInteractor = authenticationInteractor,
+            authController = authController,
+            alternateBouncerInteractor = alternateBouncerInteractor,
+            deviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor,
+            deviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor,
+            dozeScrimController = dozeScrimController,
+            keyguardBypassInteractor = keyguardBypassInteractor,
+            keyguardUpdateMonitor = keyguardUpdateMonitor,
             keyguardInteractor = keyguardInteractor,
+            sceneContainerOcclusionInteractor = sceneContainerOcclusionInteractor,
+            sceneInteractor = sceneInteractor,
+            dumpManager = dumpManager,
         )
     }
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/dreams/homecontrols/service/FakeIHomeControlsRemoteProxyBinder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/FakeIHomeControlsRemoteProxyBinder.kt
new file mode 100644
index 0000000..f469d74
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/FakeIHomeControlsRemoteProxyBinder.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.dreams.homecontrols.service
+
+import android.content.ComponentName
+import com.android.systemui.dreams.homecontrols.shared.IHomeControlsRemoteProxy
+import com.android.systemui.dreams.homecontrols.shared.IOnControlsSettingsChangeListener
+
+/** Fake binder implementation of [IHomeControlsRemoteProxy] */
+class FakeIHomeControlsRemoteProxyBinder : IHomeControlsRemoteProxy.Stub() {
+    val callbacks = mutableSetOf<IOnControlsSettingsChangeListener>()
+
+    override fun registerListenerForCurrentUser(callback: IOnControlsSettingsChangeListener) {
+        callbacks.add(callback)
+    }
+
+    override fun unregisterListenerForCurrentUser(callback: IOnControlsSettingsChangeListener) {
+        callbacks.remove(callback)
+    }
+
+    fun notifyCallbacks(component: ComponentName, allowTrivialControlsOnLockscreen: Boolean) {
+        for (callback in callbacks.toSet()) {
+            callback.onControlsSettingsChanged(component, allowTrivialControlsOnLockscreen)
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyKosmos.kt
new file mode 100644
index 0000000..58ebbf4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/HomeControlsRemoteProxyKosmos.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.dreams.homecontrols.service
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+
+val Kosmos.homeControlsRemoteProxy by
+    Kosmos.Fixture {
+        HomeControlsRemoteProxy(
+            bgScope = applicationCoroutineScope,
+            proxy = fakeHomeControlsRemoteBinder,
+            dumpManager = dumpManager,
+        )
+    }
+
+val Kosmos.fakeHomeControlsRemoteBinder by
+    Kosmos.Fixture<FakeIHomeControlsRemoteProxyBinder> { FakeIHomeControlsRemoteProxyBinder() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorKosmos.kt
new file mode 100644
index 0000000..c85c888
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/service/RemoteHomeControlsDataSourceDelegatorKosmos.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.dreams.homecontrols.service
+
+import com.android.systemui.dreams.homecontrols.dagger.HomeControlsRemoteServiceComponent
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.logcatLogBuffer
+import org.mockito.kotlin.mock
+
+val Kosmos.remoteHomeControlsDataSourceDelegator by
+    Kosmos.Fixture {
+        RemoteHomeControlsDataSourceDelegator(
+            bgScope = applicationCoroutineScope,
+            serviceFactory = homeControlsRemoteServiceFactory,
+            logBuffer = logcatLogBuffer("HomeControlsDreamInteractor"),
+            dumpManager = dumpManager,
+        )
+    }
+
+var Kosmos.homeControlsRemoteServiceFactory by
+    Kosmos.Fixture<HomeControlsRemoteServiceComponent.Factory> { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/FakeHomeControlsDataSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/FakeHomeControlsDataSource.kt
new file mode 100644
index 0000000..f8ea022
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/FakeHomeControlsDataSource.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.dreams.homecontrols.shared.model
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+
+class FakeHomeControlsDataSource : HomeControlsDataSource {
+
+    private val _componentInfo = MutableStateFlow<HomeControlsComponentInfo?>(null)
+
+    override val componentInfo: Flow<HomeControlsComponentInfo>
+        get() = _componentInfo.filterNotNull()
+
+    fun setComponentInfo(info: HomeControlsComponentInfo) {
+        _componentInfo.value = info
+    }
+}
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/dreams/homecontrols/shared/model/HomeControlsDataSourceKosmos.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/dreams/homecontrols/shared/model/HomeControlsDataSourceKosmos.kt
index 06592b1..942216b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/shared/model/HomeControlsDataSourceKosmos.kt
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.dreams.homecontrols.shared.model
 
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
+val Kosmos.homeControlsDataSource by
+    Kosmos.Fixture<HomeControlsDataSource> { fakeHomeControlsDataSource }
+
+val Kosmos.fakeHomeControlsDataSource by
+    Kosmos.Fixture<FakeHomeControlsDataSource> { FakeHomeControlsDataSource() }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorKosmos.kt
similarity index 75%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorKosmos.kt
index 7d7841f..a1182a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsComponentInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/homecontrols/system/domain/interactor/HomeControlsComponentInteractorKosmos.kt
@@ -13,21 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.dreams.homecontrols
+package com.android.systemui.dreams.homecontrols.system.domain.interactor
 
-import android.os.powerManager
-import android.service.dream.dreamManager
-import com.android.systemui.common.domain.interactor.packageChangeInteractor
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.panels.authorizedPanelsRepository
 import com.android.systemui.controls.panels.selectedComponentRepository
-import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.time.fakeSystemClock
 
 val Kosmos.homeControlsComponentInteractor by
     Kosmos.Fixture {
@@ -37,10 +32,6 @@
             authorizedPanelsRepository = authorizedPanelsRepository,
             userRepository = fakeUserRepository,
             bgScope = applicationCoroutineScope,
-            systemClock = fakeSystemClock,
-            powerManager = powerManager,
-            dreamManager = dreamManager,
-            packageChangeInteractor = packageChangeInteractor,
         )
     }
 
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/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactoryKosmos.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/keyguard/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactoryKosmos.kt
index 06592b1..fd882a8 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/data/quickaffordance/KeyguardQuickAffordanceProviderClientFactoryKosmos.kt
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.keyguard.data.quickaffordance
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.settings.userTracker
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
+val Kosmos.keyguardQuickAffordanceProviderClientFactory by
+    Kosmos.Fixture { FakeKeyguardQuickAffordanceProviderClientFactory(userTracker) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/LocalUserSelectionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/LocalUserSelectionManagerKosmos.kt
new file mode 100644
index 0000000..21d1a76
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/LocalUserSelectionManagerKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import android.content.applicationContext
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.settings.userFileManager
+import com.android.systemui.settings.userTracker
+
+val Kosmos.localUserSelectionManager by
+    Kosmos.Fixture {
+        KeyguardQuickAffordanceLocalUserSelectionManager(
+            context = applicationContext,
+            userFileManager = userFileManager,
+            userTracker = userTracker,
+            broadcastDispatcher = broadcastDispatcher,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/RemoteUserSelectionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/RemoteUserSelectionManagerKosmos.kt
new file mode 100644
index 0000000..ec63071
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/quickaffordance/RemoteUserSelectionManagerKosmos.kt
@@ -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 com.android.systemui.keyguard.data.quickaffordance
+
+import android.os.UserHandle
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.userTracker
+
+val Kosmos.remoteUserSelectionManager by
+    Kosmos.Fixture {
+        KeyguardQuickAffordanceRemoteUserSelectionManager(
+            scope = testScope.backgroundScope,
+            userTracker = userTracker,
+            clientFactory = keyguardQuickAffordanceProviderClientFactory,
+            userHandle = UserHandle.SYSTEM,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryKosmos.kt
index 9bbb34c..84eea62 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryKosmos.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.keyguard.data.repository
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.statusbar.policy.devicePostureController
 
 val Kosmos.devicePostureRepository: DevicePostureRepository by
-    Kosmos.Fixture { FakeDevicePostureRepository() }
+    Kosmos.Fixture { DevicePostureRepositoryImpl(devicePostureController, testDispatcher) }
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 4976cc2..19e077c 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
@@ -53,6 +53,15 @@
     private val initInLockscreen: Boolean = true,
 
     /**
+     * Initial value for [FakeKeyguardTransitionRepository.sendTransitionStepsOnStartTransition].
+     * This needs to be configurable in the constructor since some transitions are triggered on
+     * init, before a test has the chance to set sendTransitionStepsOnStartTransition to false.
+     */
+    private val initiallySendTransitionStepsOnStartTransition: Boolean = true,
+    private val testScope: TestScope,
+) : KeyguardTransitionRepository {
+
+    /**
      * If true, calls to [startTransition] will automatically emit STARTED, RUNNING, and FINISHED
      * transition steps from/to the given states.
      *
@@ -64,11 +73,9 @@
      *
      * If your test needs to make assertions at specific points between STARTED/FINISHED, or if it's
      * difficult to set up all of the conditions to make the transition interactors actually call
-     * startTransition, then construct a FakeKeyguardTransitionRepository with this value false.
+     * startTransition, set this value to false.
      */
-    private val sendTransitionStepsOnStartTransition: Boolean = true,
-    private val testScope: TestScope,
-) : KeyguardTransitionRepository {
+    var sendTransitionStepsOnStartTransition = initiallySendTransitionStepsOnStartTransition
 
     private val _transitions =
         MutableSharedFlow<TransitionStep>(replay = 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
@@ -77,7 +84,11 @@
     @Inject
     constructor(
         testScope: TestScope
-    ) : this(initInLockscreen = true, sendTransitionStepsOnStartTransition = true, testScope)
+    ) : this(
+        initInLockscreen = true,
+        initiallySendTransitionStepsOnStartTransition = true,
+        testScope
+    )
 
     private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
         MutableStateFlow(
@@ -176,6 +187,20 @@
         testScheduler: TestCoroutineScheduler,
         throughTransitionState: TransitionState = TransitionState.FINISHED,
     ) {
+        val lastStep = _transitions.replayCache.lastOrNull()
+        if (lastStep != null && lastStep.transitionState != TransitionState.FINISHED) {
+            sendTransitionStep(
+                step =
+                TransitionStep(
+                    transitionState = TransitionState.CANCELED,
+                    from = lastStep.from,
+                    to = lastStep.to,
+                    value = 0f,
+                )
+            )
+            testScheduler.runCurrent()
+        }
+
         sendTransitionStep(
             step =
                 TransitionStep(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBypassRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBypassRepositoryKosmos.kt
new file mode 100644
index 0000000..c91823c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBypassRepositoryKosmos.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.content.testableContext
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
+import com.android.systemui.tuner.tunerService
+import com.android.systemui.util.mockito.withArgCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+val Kosmos.keyguardBypassRepository: KeyguardBypassRepository by Fixture {
+    KeyguardBypassRepository(
+        testableContext.resources,
+        biometricSettingsRepository,
+        devicePostureRepository,
+        dumpManager,
+        tunerService,
+        testDispatcher,
+    )
+}
+
+fun Kosmos.configureKeyguardBypass(
+    isBypassAvailable: Boolean? = null,
+    faceAuthEnrolledAndEnabled: Boolean = true,
+    faceUnlockBypassOverrideConfig: Int = 0, /* FACE_UNLOCK_BYPASS_NO_OVERRIDE */
+    faceAuthPostureConfig: Int = DEVICE_POSTURE_UNKNOWN,
+) {
+    when (isBypassAvailable) {
+        null -> {
+            biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(faceAuthEnrolledAndEnabled)
+            testableContext.orCreateTestableResources.addOverride(
+                R.integer.config_face_unlock_bypass_override,
+                faceUnlockBypassOverrideConfig,
+            )
+            testableContext.orCreateTestableResources.addOverride(
+                R.integer.config_face_auth_supported_posture,
+                faceAuthPostureConfig,
+            )
+        }
+        true -> {
+            biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+            testableContext.orCreateTestableResources.addOverride(
+                R.integer.config_face_unlock_bypass_override,
+                1, /* FACE_UNLOCK_BYPASS_ALWAYS */
+            )
+            testableContext.orCreateTestableResources.addOverride(
+                R.integer.config_face_auth_supported_posture,
+                DEVICE_POSTURE_UNKNOWN,
+            )
+        }
+        false -> {
+            biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false)
+            testableContext.orCreateTestableResources.addOverride(
+                R.integer.config_face_unlock_bypass_override,
+                2, /* FACE_UNLOCK_BYPASS_NEVER */
+            )
+            testableContext.orCreateTestableResources.addOverride(
+                R.integer.config_face_auth_supported_posture,
+                DEVICE_POSTURE_CLOSED,
+            )
+        }
+    }
+}
+
+fun DevicePostureController.verifyCallback() =
+    withArgCaptor<DevicePostureController.Callback> {
+        verify(this@verifyCallback).addCallback(capture())
+    }
+
+fun DevicePostureController.verifyNoCallback() =
+    verify(this@verifyNoCallback, never()).addCallback(any())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryKosmos.kt
new file mode 100644
index 0000000..a6d4ec5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryKosmos.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.content.applicationContext
+import android.os.UserHandle
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.keyguard.data.quickaffordance.localUserSelectionManager
+import com.android.systemui.keyguard.data.quickaffordance.remoteUserSelectionManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.userTracker
+import org.mockito.kotlin.mock
+
+val Kosmos.keyguardQuickAffordanceRepository by Fixture {
+    KeyguardQuickAffordanceRepository(
+        appContext = applicationContext,
+        scope = testScope.backgroundScope,
+        localUserSelectionManager = localUserSelectionManager,
+        remoteUserSelectionManager = remoteUserSelectionManager,
+        userTracker = userTracker,
+        legacySettingSyncer = mock(),
+        configs = setOf(),
+        dumpManager = dumpManager,
+        userHandle = UserHandle.SYSTEM,
+    )
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/PulseExpansionRepositoryKosmos.kt
similarity index 69%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/PulseExpansionRepositoryKosmos.kt
index 83cbc52..1851774 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/PulseExpansionRepositoryKosmos.kt
@@ -13,10 +13,11 @@
  * 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.keyguard.data.repository
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.pulseExpansionRepository: PulseExpansionRepository by
+    Kosmos.Fixture { PulseExpansionRepository(dumpManager) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractorKosmos.kt
new file mode 100644
index 0000000..59ef9df
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBypassInteractorKosmos.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.keyguard.data.repository.keyguardBypassRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+
+val Kosmos.keyguardBypassInteractor by Fixture {
+    KeyguardBypassInteractor(
+        keyguardBypassRepository,
+        alternateBouncerInteractor,
+        keyguardQuickAffordanceInteractor,
+        pulseExpansionInteractor,
+        sceneInteractor,
+        shadeInteractor,
+        dumpManager,
+    )
+}
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/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt
new file mode 100644
index 0000000..009d17e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorKosmos.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.app.admin.devicePolicyManager
+import android.content.applicationContext
+import com.android.internal.widget.lockPatternUtils
+import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
+import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.dock.dockManager
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.keyguardQuickAffordanceRepository
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.settings.userTracker
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.policy.keyguardStateController
+import org.mockito.kotlin.mock
+
+var Kosmos.keyguardQuickAffordanceInteractor by Fixture {
+    KeyguardQuickAffordanceInteractor(
+        keyguardInteractor = keyguardInteractor,
+        shadeInteractor = shadeInteractor,
+        lockPatternUtils = lockPatternUtils,
+        keyguardStateController = keyguardStateController,
+        userTracker = userTracker,
+        activityStarter = activityStarter,
+        featureFlags = featureFlagsClassic,
+        repository = { keyguardQuickAffordanceRepository },
+        launchAnimator = dialogTransitionAnimator,
+        logger = mock<KeyguardQuickAffordancesLogger>(),
+        metricsLogger = mock<KeyguardQuickAffordancesMetricsLogger>(),
+        devicePolicyManager = devicePolicyManager,
+        dockManager = dockManager,
+        biometricSettingsRepository = biometricSettingsRepository,
+        backgroundDispatcher = testDispatcher,
+        appContext = applicationContext,
+        sceneInteractor = { sceneInteractor },
+    )
+}
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/PulseExpansionInteractorKosmos.kt
similarity index 66%
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/PulseExpansionInteractorKosmos.kt
index 06592b1..f02e0a4 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/PulseExpansionInteractorKosmos.kt
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.keyguard.data.repository.pulseExpansionRepository
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
+val Kosmos.pulseExpansionInteractor: PulseExpansionInteractor by
+    Kosmos.Fixture { PulseExpansionInteractor(pulseExpansionRepository, dumpManager) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 3c87106..3ab686d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -41,6 +42,7 @@
         communalInteractor = communalInteractor,
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         notificationsKeyguardInteractor = notificationsKeyguardInteractor,
+        pulseExpansionInteractor = pulseExpansionInteractor,
         aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel,
         notificationShadeWindowModel = notificationShadeWindowModel,
         alternateBouncerToAodTransitionViewModel = alternateBouncerToAodTransitionViewModel,
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 a9a80b5..ddae581 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
@@ -6,6 +6,8 @@
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 
 var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
 
@@ -34,3 +36,12 @@
     testScope.backgroundScope.coroutineContext
 }
 var Kosmos.mainCoroutineContext: CoroutineContext by Fixture { testScope.coroutineContext }
+
+/**
+ * Run this test body with a [Kosmos] as receiver, and using the [testScope] currently installed in
+ * that kosmos instance
+ */
+fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) =
+    testScope.runTest { this@runTest.testBody() }
+
+fun Kosmos.runCurrent() = testScope.runCurrent()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 522c387..5eaa198 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -50,6 +50,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.pulseExpansionInteractor
 import com.android.systemui.model.sceneContainerPlugin
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.power.data.repository.fakePowerRepository
@@ -124,6 +125,7 @@
     val sceneBackInteractor by lazy { kosmos.sceneBackInteractor }
     val falsingCollector by lazy { kosmos.falsingCollector }
     val powerInteractor by lazy { kosmos.powerInteractor }
+    val pulseExpansionInteractor by lazy { kosmos.pulseExpansionInteractor }
     val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
     val deviceEntryUdfpsInteractor by lazy { kosmos.deviceEntryUdfpsInteractor }
     val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplKosmos.kt
similarity index 70%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplKosmos.kt
index 83cbc52..c337298 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplKosmos.kt
@@ -13,10 +13,11 @@
  * 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.media.controls.domain.pipeline
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+/** Empty mock */
+val Kosmos.legacyMediaDataManagerImpl by Kosmos.Fixture { mock<LegacyMediaDataManagerImpl>() }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerKosmos.kt
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerKosmos.kt
index 83cbc52..f733da1 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerKosmos.kt
@@ -13,10 +13,9 @@
  * 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.media.controls.domain.pipeline
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.mediaDataManager: MediaDataManager by Kosmos.Fixture()
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerKosmos.kt
similarity index 71%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerKosmos.kt
index 83cbc52..3c35ce9 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerKosmos.kt
@@ -13,10 +13,11 @@
  * 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.media.controls.ui.controller
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+/** Empty mock */
+val Kosmos.mediaCarouselController by Kosmos.Fixture { mock<MediaCarouselController>() }
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/media/controls/ui/controller/MediaCarouselControllerLoggerKosmos.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/media/controls/ui/controller/MediaCarouselControllerLoggerKosmos.kt
index 06592b1..9f5a832 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLoggerKosmos.kt
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.media.controls.ui.controller
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.core.FakeLogBuffer
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
+val Kosmos.mediaCarouselControllerLogger by
+    Kosmos.Fixture { MediaCarouselControllerLogger(FakeLogBuffer.Factory.create()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
index 7c24b4c..624e174 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
@@ -16,8 +16,15 @@
 
 package com.android.systemui.media.controls.ui.controller
 
+import android.content.testableContext
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.animation.UniqueObjectHostView
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
 
-var Kosmos.mediaHierarchyManager by Fixture { mock<MediaHierarchyManager>() }
+var Kosmos.mediaHierarchyManager by Fixture {
+    mock<MediaHierarchyManager> {
+        on { register(any()) }.thenAnswer { UniqueObjectHostView(this@Fixture.testableContext) }
+    }
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManagerKosmos.kt
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManagerKosmos.kt
index 83cbc52..8b02c57 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManagerKosmos.kt
@@ -13,10 +13,9 @@
  * 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.media.controls.ui.controller
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.mediaHostStatesManager by Kosmos.Fixture { MediaHostStatesManager() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/view/MediaHostKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/view/MediaHostKosmos.kt
new file mode 100644
index 0000000..9638e11
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/view/MediaHostKosmos.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.media.controls.ui.view
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.domain.pipeline.mediaDataManager
+import com.android.systemui.media.controls.ui.controller.mediaCarouselController
+import com.android.systemui.media.controls.ui.controller.mediaCarouselControllerLogger
+import com.android.systemui.media.controls.ui.controller.mediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager
+import java.util.function.Supplier
+
+private val Kosmos.mediaHostProvider by
+    Kosmos.Fixture {
+        Supplier<MediaHost> {
+            MediaHost(
+                MediaHost.MediaHostStateHolder(),
+                mediaHierarchyManager,
+                mediaDataManager,
+                mediaHostStatesManager,
+                mediaCarouselController,
+                mediaCarouselControllerLogger,
+            )
+        }
+    }
+
+val Kosmos.qqsMediaHost by Kosmos.Fixture { mediaHostProvider.get() }
+val Kosmos.qsMediaHost by Kosmos.Fixture { mediaHostProvider.get() }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModuleKosmos.kt
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModuleKosmos.kt
index 83cbc52..fb8ffb0 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModuleKosmos.kt
@@ -13,10 +13,9 @@
  * 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.qs.composefragment.dagger
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.usingMediaInComposeFragment by Kosmos.Fixture<Boolean>()
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..bda3192 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
@@ -22,11 +22,14 @@
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.ui.view.qqsMediaHost
+import com.android.systemui.media.controls.ui.view.qsMediaHost
+import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment
 import com.android.systemui.qs.footerActionsController
 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.panels.ui.viewmodel.inFirstPageViewModel
+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 +44,7 @@
                 lifecycleScope: LifecycleCoroutineScope
             ): QSFragmentComposeViewModel {
                 return QSFragmentComposeViewModel(
-                    quickSettingsContainerViewModel,
+                    quickSettingsContainerViewModelFactory,
                     mainResources,
                     footerActionsViewModelFactory,
                     footerActionsController,
@@ -53,7 +56,10 @@
                     configurationInteractor,
                     largeScreenHeaderHelper,
                     tileSquishinessInteractor,
-                    paginatedGridViewModel,
+                    inFirstPageViewModel,
+                    qqsMediaHost,
+                    qsMediaHost,
+                    usingMediaInComposeFragment,
                     lifecycleScope,
                 )
             }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModelKosmos.kt
similarity index 73%
rename from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModelKosmos.kt
index 83cbc52..1fa74ca 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InFirstPageViewModelKosmos.kt
@@ -13,10 +13,9 @@
  * 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.qs.panels.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.inFirstPageViewModel by Kosmos.Fixture { InFirstPageViewModel() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index 48ef57e..5c8ca83 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.panels.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.qs.panels.domain.interactor.paginatedGridInteractor
 
 val Kosmos.paginatedGridViewModel by
@@ -26,6 +25,6 @@
             iconTilesViewModel,
             qsColumnsViewModel,
             paginatedGridInteractor,
-            applicationCoroutineScope,
+            inFirstPageViewModel,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
index 41ee260..20be5c6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelKosmos.kt
@@ -18,19 +18,21 @@
 
 import com.android.systemui.haptics.msdl.tileHapticsViewModelFactoryProvider
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.qs.panels.domain.interactor.quickQuickSettingsRowInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
 
-val Kosmos.quickQuickSettingsViewModel by
+val Kosmos.quickQuickSettingsViewModelFactory by
     Kosmos.Fixture {
-        QuickQuickSettingsViewModel(
-            currentTilesInteractor,
-            qsColumnsViewModel,
-            quickQuickSettingsRowInteractor,
-            tileSquishinessViewModel,
-            iconTilesViewModel,
-            applicationCoroutineScope,
-            tileHapticsViewModelFactoryProvider,
-        )
+        object : QuickQuickSettingsViewModel.Factory {
+            override fun create(): QuickQuickSettingsViewModel {
+                return QuickQuickSettingsViewModel(
+                    currentTilesInteractor,
+                    qsColumnsViewModel,
+                    quickQuickSettingsRowInteractor,
+                    tileSquishinessViewModel,
+                    iconTilesViewModel,
+                    tileHapticsViewModelFactoryProvider,
+                )
+            }
+        }
     }
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..ce103ec 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.quickQuickSettingsViewModelFactory
 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,
+                    quickQuickSettingsViewModelFactory,
+                    supportsBrightnessMirroring,
+                    tileGridViewModel,
+                    editModeViewModel,
+                )
+            }
+        }
     }
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/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index 4f414d9..3300c96 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -17,6 +17,7 @@
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
 import com.android.systemui.scene.ui.viewmodel.splitEdgeDetector
 import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
 import kotlinx.coroutines.flow.MutableStateFlow
 import org.mockito.kotlin.mock
 
@@ -87,6 +88,7 @@
                 falsingInteractor = falsingInteractor,
                 powerInteractor = powerInteractor,
                 shadeInteractor = shadeInteractor,
+                remoteInputInteractor = remoteInputInteractor,
                 splitEdgeDetector = splitEdgeDetector,
                 logger = sceneLogger,
                 hapticsViewModelFactory = sceneContainerHapticsViewModelFactory,
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/shade/CameraLauncherKosmos.kt
similarity index 62%
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/shade/CameraLauncherKosmos.kt
index 06592b1..d6dd867 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/CameraLauncherKosmos.kt
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.ui.viewmodel
+package com.android.systemui.shade
 
+import com.android.systemui.camera.cameraGestureHelper
+import com.android.systemui.keyguard.domain.interactor.keyguardQuickAffordanceInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.keyguardBypassController
 
-val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
+val Kosmos.cameraLauncher by
     Kosmos.Fixture {
-        QuickSettingsShadeUserActionsViewModel(
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
+        CameraLauncher(cameraGestureHelper, keyguardBypassController) {
+            keyguardQuickAffordanceInteractor
+        }
     }
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/data/repository/FakeRemoteInputRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
index 91602c2..375bede 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
@@ -23,6 +23,11 @@
 class FakeRemoteInputRepository : RemoteInputRepository {
     override val isRemoteInputActive = MutableStateFlow(false)
     override val remoteInputRowBottomBound: Flow<Float?> = flowOf(null)
+    var areRemoteInputsClosed: Boolean = false
 
     override fun setRemoteInputRowBottomBound(bottom: Float?) {}
+
+    override fun closeRemoteInputs() {
+        areRemoteInputsClosed = true
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
index 61a38b8..9c2a2be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationsKeyguardInteractorKosmos.kt
@@ -22,7 +22,5 @@
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 
 val Kosmos.notificationsKeyguardInteractor by Fixture {
-    NotificationsKeyguardInteractor(
-        repository = notificationsKeyguardViewStateRepository,
-    )
+    NotificationsKeyguardInteractor(repository = notificationsKeyguardViewStateRepository)
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt
index 8785256..87ea147 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt
@@ -19,6 +19,7 @@
 import android.content.applicationContext
 import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -43,5 +44,6 @@
             mock<UnlockedScreenOffAnimationController>(),
             primaryBouncerInteractor,
             alternateBouncerInteractor,
+            communalSceneInteractor,
         )
     }
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/packages/SystemUI/tests/utils/src/com/android/systemui/tuner/TunerServiceKosmos.kt
similarity index 73%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/tuner/TunerServiceKosmos.kt
index 83cbc52..9bc3ae9 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/tuner/TunerServiceKosmos.kt
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.platform.test.ravenwood;
+package com.android.systemui.tuner
 
-/** Stub class. The actual implementaetion is in junit-impl-src. */
-public class RavenwoodRunnerState {
-    public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) {
-    }
-}
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import org.mockito.kotlin.mock
+
+val Kosmos.tunerService by Fixture { mock<TunerService>() }
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/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/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 8663593..44ea9a2 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -945,10 +945,11 @@
                 String tag = parser.getName();
                 if (!sectionTag.equals(tag)) continue;
                 for (Pair<Integer, String> pair : List.of(
-                        new Pair<>(WallpaperManager.PORTRAIT, "Portrait"),
-                        new Pair<>(WallpaperManager.LANDSCAPE, "Landscape"),
-                        new Pair<>(WallpaperManager.SQUARE_PORTRAIT, "SquarePortrait"),
-                        new Pair<>(WallpaperManager.SQUARE_LANDSCAPE, "SquareLandscape"))) {
+                        new Pair<>(WallpaperManager.ORIENTATION_PORTRAIT, "Portrait"),
+                        new Pair<>(WallpaperManager.ORIENTATION_LANDSCAPE, "Landscape"),
+                        new Pair<>(WallpaperManager.ORIENTATION_SQUARE_PORTRAIT, "SquarePortrait"),
+                        new Pair<>(WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE,
+                                "SquareLandscape"))) {
                     Rect cropHint = new Rect(
                             getAttributeInt(parser, "cropLeft" + pair.second, 0),
                             getAttributeInt(parser, "cropTop" + pair.second, 0),
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 3ecdf3f..f5fb644 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -827,16 +827,17 @@
 
     @Test
     public void testOnRestore_singleCropHint() throws Exception {
-        Map<Integer, Rect> testMap = Map.of(WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4));
+        Map<Integer, Rect> testMap = Map.of(
+                WallpaperManager.ORIENTATION_PORTRAIT, new Rect(1, 2, 3, 4));
         testParseCropHints(testMap);
     }
 
     @Test
     public void testOnRestore_multipleCropHints() throws Exception {
         Map<Integer, Rect> testMap = Map.of(
-                WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4),
-                WallpaperManager.SQUARE_PORTRAIT, new Rect(5, 6, 7, 8),
-                WallpaperManager.SQUARE_LANDSCAPE, new Rect(9, 10, 11, 12));
+                WallpaperManager.ORIENTATION_PORTRAIT, new Rect(1, 2, 3, 4),
+                WallpaperManager.ORIENTATION_SQUARE_PORTRAIT, new Rect(5, 6, 7, 8),
+                WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE, new Rect(9, 10, 11, 12));
         testParseCropHints(testMap);
     }
 
@@ -936,10 +937,10 @@
         out.startTag(null, "wp");
         for (Map.Entry<Integer, Rect> entry: crops.entrySet()) {
             String orientation = switch (entry.getKey()) {
-                case WallpaperManager.PORTRAIT -> "Portrait";
-                case WallpaperManager.LANDSCAPE -> "Landscape";
-                case WallpaperManager.SQUARE_PORTRAIT -> "SquarePortrait";
-                case WallpaperManager.SQUARE_LANDSCAPE -> "SquareLandscape";
+                case WallpaperManager.ORIENTATION_PORTRAIT -> "Portrait";
+                case WallpaperManager.ORIENTATION_LANDSCAPE -> "Landscape";
+                case WallpaperManager.ORIENTATION_SQUARE_PORTRAIT -> "SquarePortrait";
+                case WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE -> "SquareLandscape";
                 default -> throw new IllegalArgumentException("Invalid orientation");
             };
             Rect rect = entry.getValue();
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 9a5e623b..b3f78ab 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",
 }
 
@@ -360,6 +360,239 @@
     ],
 }
 
+filegroup {
+    name: "ravenwood-data",
+    device_common_srcs: [
+        ":system-build.prop",
+        ":framework-res",
+        ":ravenwood-empty-res",
+        ":framework-platform-compat-config",
+        ":services-platform-compat-config",
+    ],
+    device_first_srcs: [
+        ":apex_icu.dat",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+// Keep in sync with build/make/target/product/generic/Android.bp
+filegroup {
+    name: "ravenwood-fonts",
+    device_common_srcs: [
+        ":AndroidClock.ttf",
+        ":CarroisGothicSC-Regular.ttf",
+        ":ComingSoon.ttf",
+        ":CutiveMono.ttf",
+        ":DancingScript-Regular.ttf",
+        ":DroidSansMono.ttf",
+        ":NotoColorEmoji.ttf",
+        ":NotoColorEmojiFlags.ttf",
+        ":NotoNaskhArabic-Bold.ttf",
+        ":NotoNaskhArabic-Regular.ttf",
+        ":NotoNaskhArabicUI-Bold.ttf",
+        ":NotoNaskhArabicUI-Regular.ttf",
+        ":NotoSansAdlam-VF.ttf",
+        ":NotoSansAhom-Regular.otf",
+        ":NotoSansAnatolianHieroglyphs-Regular.otf",
+        ":NotoSansArmenian-VF.ttf",
+        ":NotoSansAvestan-Regular.ttf",
+        ":NotoSansBalinese-Regular.ttf",
+        ":NotoSansBamum-Regular.ttf",
+        ":NotoSansBassaVah-Regular.otf",
+        ":NotoSansBatak-Regular.ttf",
+        ":NotoSansBengali-VF.ttf",
+        ":NotoSansBengaliUI-VF.ttf",
+        ":NotoSansBhaiksuki-Regular.otf",
+        ":NotoSansBrahmi-Regular.ttf",
+        ":NotoSansBuginese-Regular.ttf",
+        ":NotoSansBuhid-Regular.ttf",
+        ":NotoSansCJK-Regular.ttc",
+        ":NotoSansCanadianAboriginal-Regular.ttf",
+        ":NotoSansCarian-Regular.ttf",
+        ":NotoSansChakma-Regular.otf",
+        ":NotoSansCham-Bold.ttf",
+        ":NotoSansCham-Regular.ttf",
+        ":NotoSansCherokee-Regular.ttf",
+        ":NotoSansCoptic-Regular.ttf",
+        ":NotoSansCuneiform-Regular.ttf",
+        ":NotoSansCypriot-Regular.ttf",
+        ":NotoSansDeseret-Regular.ttf",
+        ":NotoSansDevanagari-VF.ttf",
+        ":NotoSansDevanagariUI-VF.ttf",
+        ":NotoSansEgyptianHieroglyphs-Regular.ttf",
+        ":NotoSansElbasan-Regular.otf",
+        ":NotoSansEthiopic-VF.ttf",
+        ":NotoSansGeorgian-VF.ttf",
+        ":NotoSansGlagolitic-Regular.ttf",
+        ":NotoSansGothic-Regular.ttf",
+        ":NotoSansGrantha-Regular.ttf",
+        ":NotoSansGujarati-Bold.ttf",
+        ":NotoSansGujarati-Regular.ttf",
+        ":NotoSansGujaratiUI-Bold.ttf",
+        ":NotoSansGujaratiUI-Regular.ttf",
+        ":NotoSansGunjalaGondi-Regular.otf",
+        ":NotoSansGurmukhi-VF.ttf",
+        ":NotoSansGurmukhiUI-VF.ttf",
+        ":NotoSansHanifiRohingya-Regular.otf",
+        ":NotoSansHanunoo-Regular.ttf",
+        ":NotoSansHatran-Regular.otf",
+        ":NotoSansHebrew-Bold.ttf",
+        ":NotoSansHebrew-Regular.ttf",
+        ":NotoSansImperialAramaic-Regular.ttf",
+        ":NotoSansInscriptionalPahlavi-Regular.ttf",
+        ":NotoSansInscriptionalParthian-Regular.ttf",
+        ":NotoSansJavanese-Regular.otf",
+        ":NotoSansKaithi-Regular.ttf",
+        ":NotoSansKannada-VF.ttf",
+        ":NotoSansKannadaUI-VF.ttf",
+        ":NotoSansKayahLi-Regular.ttf",
+        ":NotoSansKharoshthi-Regular.ttf",
+        ":NotoSansKhmer-VF.ttf",
+        ":NotoSansKhmerUI-Bold.ttf",
+        ":NotoSansKhmerUI-Regular.ttf",
+        ":NotoSansKhojki-Regular.otf",
+        ":NotoSansLao-Bold.ttf",
+        ":NotoSansLao-Regular.ttf",
+        ":NotoSansLaoUI-Bold.ttf",
+        ":NotoSansLaoUI-Regular.ttf",
+        ":NotoSansLepcha-Regular.ttf",
+        ":NotoSansLimbu-Regular.ttf",
+        ":NotoSansLinearA-Regular.otf",
+        ":NotoSansLinearB-Regular.ttf",
+        ":NotoSansLisu-Regular.ttf",
+        ":NotoSansLycian-Regular.ttf",
+        ":NotoSansLydian-Regular.ttf",
+        ":NotoSansMalayalam-VF.ttf",
+        ":NotoSansMalayalamUI-VF.ttf",
+        ":NotoSansMandaic-Regular.ttf",
+        ":NotoSansManichaean-Regular.otf",
+        ":NotoSansMarchen-Regular.otf",
+        ":NotoSansMasaramGondi-Regular.otf",
+        ":NotoSansMedefaidrin-VF.ttf",
+        ":NotoSansMeeteiMayek-Regular.ttf",
+        ":NotoSansMeroitic-Regular.otf",
+        ":NotoSansMiao-Regular.otf",
+        ":NotoSansModi-Regular.ttf",
+        ":NotoSansMongolian-Regular.ttf",
+        ":NotoSansMro-Regular.otf",
+        ":NotoSansMultani-Regular.otf",
+        ":NotoSansMyanmar-Bold.otf",
+        ":NotoSansMyanmar-Medium.otf",
+        ":NotoSansMyanmar-Regular.otf",
+        ":NotoSansMyanmarUI-Bold.otf",
+        ":NotoSansMyanmarUI-Medium.otf",
+        ":NotoSansMyanmarUI-Regular.otf",
+        ":NotoSansNKo-Regular.ttf",
+        ":NotoSansNabataean-Regular.otf",
+        ":NotoSansNewTaiLue-Regular.ttf",
+        ":NotoSansNewa-Regular.otf",
+        ":NotoSansOgham-Regular.ttf",
+        ":NotoSansOlChiki-Regular.ttf",
+        ":NotoSansOldItalic-Regular.ttf",
+        ":NotoSansOldNorthArabian-Regular.otf",
+        ":NotoSansOldPermic-Regular.otf",
+        ":NotoSansOldPersian-Regular.ttf",
+        ":NotoSansOldSouthArabian-Regular.ttf",
+        ":NotoSansOldTurkic-Regular.ttf",
+        ":NotoSansOriya-Bold.ttf",
+        ":NotoSansOriya-Regular.ttf",
+        ":NotoSansOriyaUI-Bold.ttf",
+        ":NotoSansOriyaUI-Regular.ttf",
+        ":NotoSansOsage-Regular.ttf",
+        ":NotoSansOsmanya-Regular.ttf",
+        ":NotoSansPahawhHmong-Regular.otf",
+        ":NotoSansPalmyrene-Regular.otf",
+        ":NotoSansPauCinHau-Regular.otf",
+        ":NotoSansPhagsPa-Regular.ttf",
+        ":NotoSansPhoenician-Regular.ttf",
+        ":NotoSansRejang-Regular.ttf",
+        ":NotoSansRunic-Regular.ttf",
+        ":NotoSansSamaritan-Regular.ttf",
+        ":NotoSansSaurashtra-Regular.ttf",
+        ":NotoSansSharada-Regular.otf",
+        ":NotoSansShavian-Regular.ttf",
+        ":NotoSansSinhala-VF.ttf",
+        ":NotoSansSinhalaUI-VF.ttf",
+        ":NotoSansSoraSompeng-Regular.otf",
+        ":NotoSansSoyombo-VF.ttf",
+        ":NotoSansSundanese-Regular.ttf",
+        ":NotoSansSylotiNagri-Regular.ttf",
+        ":NotoSansSymbols-Regular-Subsetted.ttf",
+        ":NotoSansSymbols-Regular-Subsetted2.ttf",
+        ":NotoSansSyriacEastern-Regular.ttf",
+        ":NotoSansSyriacEstrangela-Regular.ttf",
+        ":NotoSansSyriacWestern-Regular.ttf",
+        ":NotoSansTagalog-Regular.ttf",
+        ":NotoSansTagbanwa-Regular.ttf",
+        ":NotoSansTaiLe-Regular.ttf",
+        ":NotoSansTaiTham-Regular.ttf",
+        ":NotoSansTaiViet-Regular.ttf",
+        ":NotoSansTakri-VF.ttf",
+        ":NotoSansTamil-VF.ttf",
+        ":NotoSansTamilUI-VF.ttf",
+        ":NotoSansTelugu-VF.ttf",
+        ":NotoSansTeluguUI-VF.ttf",
+        ":NotoSansThaana-Bold.ttf",
+        ":NotoSansThaana-Regular.ttf",
+        ":NotoSansThai-Bold.ttf",
+        ":NotoSansThai-Regular.ttf",
+        ":NotoSansThaiUI-Bold.ttf",
+        ":NotoSansThaiUI-Regular.ttf",
+        ":NotoSansTifinagh-Regular.otf",
+        ":NotoSansUgaritic-Regular.ttf",
+        ":NotoSansVai-Regular.ttf",
+        ":NotoSansWancho-Regular.otf",
+        ":NotoSansWarangCiti-Regular.otf",
+        ":NotoSansYi-Regular.ttf",
+        ":NotoSerif-Bold.ttf",
+        ":NotoSerif-BoldItalic.ttf",
+        ":NotoSerif-Italic.ttf",
+        ":NotoSerif-Regular.ttf",
+        ":NotoSerifArmenian-VF.ttf",
+        ":NotoSerifBengali-VF.ttf",
+        ":NotoSerifCJK-Regular.ttc",
+        ":NotoSerifDevanagari-VF.ttf",
+        ":NotoSerifDogra-Regular.ttf",
+        ":NotoSerifEthiopic-VF.ttf",
+        ":NotoSerifGeorgian-VF.ttf",
+        ":NotoSerifGujarati-VF.ttf",
+        ":NotoSerifGurmukhi-VF.ttf",
+        ":NotoSerifHebrew-Bold.ttf",
+        ":NotoSerifHebrew-Regular.ttf",
+        ":NotoSerifHentaigana.ttf",
+        ":NotoSerifKannada-VF.ttf",
+        ":NotoSerifKhmer-Bold.otf",
+        ":NotoSerifKhmer-Regular.otf",
+        ":NotoSerifLao-Bold.ttf",
+        ":NotoSerifLao-Regular.ttf",
+        ":NotoSerifMalayalam-VF.ttf",
+        ":NotoSerifMyanmar-Bold.otf",
+        ":NotoSerifMyanmar-Regular.otf",
+        ":NotoSerifNyiakengPuachueHmong-VF.ttf",
+        ":NotoSerifSinhala-VF.ttf",
+        ":NotoSerifTamil-VF.ttf",
+        ":NotoSerifTelugu-VF.ttf",
+        ":NotoSerifThai-Bold.ttf",
+        ":NotoSerifThai-Regular.ttf",
+        ":NotoSerifTibetan-VF.ttf",
+        ":NotoSerifYezidi-VF.ttf",
+        ":Roboto-Regular.ttf",
+        ":RobotoFlex-Regular.ttf",
+        ":RobotoStatic-Regular.ttf",
+        ":SourceSansPro-Bold.ttf",
+        ":SourceSansPro-BoldItalic.ttf",
+        ":SourceSansPro-Italic.ttf",
+        ":SourceSansPro-Regular.ttf",
+        ":SourceSansPro-SemiBold.ttf",
+        ":SourceSansPro-SemiBoldItalic.ttf",
+    ],
+    device_first_srcs: [
+        ":font_fallback.xml",
+        ":fonts.xml",
+    ],
+    visibility: ["//visibility:private"],
+}
+
 // JARs in "ravenwood-runtime" are set to the classpath, sorted alphabetically.
 // Rename some of the dependencies to make sure they're included in the intended order.
 
@@ -386,13 +619,8 @@
 
 android_ravenwood_libgroup {
     name: "ravenwood-runtime",
-    data: [
-        ":system-build.prop",
-        ":framework-res",
-        ":ravenwood-empty-res",
-        ":framework-platform-compat-config",
-        ":services-platform-compat-config",
-    ],
+    data: [":ravenwood-data"],
+    fonts: [":ravenwood-fonts"],
     libs: [
         "100-framework-minus-apex.ravenwood",
         "200-kxml2-android",
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 72f62c5..a1243e3 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" },
@@ -35,18 +35,18 @@
     },
     {
       "name": "CarLibHostUnitTest",
-      "host": true,
-      "keywords": ["automotive_code_coverage"]
+      "keywords": ["automotive_code_coverage"],
+      "host": true
     },
     {
       "name": "CarServiceHostUnitTest",
-      "host": true,
-      "keywords": ["automotive_code_coverage"]
+      "keywords": ["automotive_code_coverage"],
+      "host": true
     },
     {
       "name": "CarSystemUIRavenTests",
-      "host": true,
-      "keywords": ["automotive_code_coverage"]
+      "keywords": ["automotive_code_coverage"],
+      "host": true
     },
     {
       "name": "CtsAccountManagerTestCasesRavenwood",
@@ -65,6 +65,10 @@
       "host": true
     },
     {
+      "name": "CtsDeviceConfigTestCasesRavenwood",
+      "host": true
+    },
+    {
       "name": "CtsGraphicsTestCasesRavenwood",
       "host": true
     },
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 e0f9ec9..0000000
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ /dev/null
@@ -1,209 +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.AssumptionViolatedException;
-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();
-
-            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 {@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..0f16352 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;
 
@@ -165,20 +165,26 @@
         RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP);
         setSystemProperties(null);
 
+        // Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
+        // before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
+        if (RAVENWOOD_VERBOSE_LOGGING) {
+            RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging");
+            try {
+                Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
+            } catch (ErrnoException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
         // Make sure libandroid_runtime is loaded.
         RavenwoodNativeLoader.loadFrameworkNativeCode();
 
         // Redirect stdout/stdin to liblog.
         RuntimeInit.redirectLogStreams();
 
-        if (RAVENWOOD_VERBOSE_LOGGING) {
-            RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging");
-            try {
-                Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
-            } catch (ErrnoException e) {
-                // Shouldn't happen.
-            }
-        }
+        // Touch some references early to ensure they're <clinit>'ed
+        Objects.requireNonNull(Build.TYPE);
+        Objects.requireNonNull(Build.VERSION.SDK);
 
         System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
         // This will let AndroidJUnit4 use the original runner.
@@ -191,12 +197,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 +218,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 +225,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 +278,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 +295,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 +312,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-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/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index 3ff0848..2a3c26e 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -173,6 +173,24 @@
     throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0));
 }
 
+static void maybeRedirectLog() {
+    auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
+    if (ravenwoodLogOut == NULL) {
+        return;
+    }
+    ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
+
+    // Redirect stdin / stdout to /dev/tty.
+    int ttyFd = open(ravenwoodLogOut, O_WRONLY);
+    if (ttyFd == -1) {
+        ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
+                strerror(errno));
+        return;
+    }
+    dup2(ttyFd, 1);
+    dup2(ttyFd, 2);
+}
+
 // ---- Registration ----
 
 extern void register_android_system_OsConstants(JNIEnv* env);
@@ -192,6 +210,8 @@
 };
 
 extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+    maybeRedirectLog();
+
     ALOGI("%s: JNI_OnLoad", __FILE__);
 
     JNIEnv* env = GetJNIEnvOrDie(vm);
diff --git a/ravenwood/scripts/update-test-mapping.sh b/ravenwood/scripts/update-test-mapping.sh
index e478b50..ab37baf 100755
--- a/ravenwood/scripts/update-test-mapping.sh
+++ b/ravenwood/scripts/update-test-mapping.sh
@@ -23,6 +23,14 @@
 # Tests that shouldn't be in presubmit.
 EXEMPT='^(SystemUiRavenTests)$'
 
+is_car() {
+    local module="$1"
+
+    # If the module name starts with "Car", then it's a test for "Car".
+    [[ "$module" =~ ^Car ]]
+    return $?
+}
+
 main() {
     local script_name="${0##*/}"
     local script_dir="${0%/*}"
@@ -62,6 +70,10 @@
             fi
             echo "    {"
             echo "      \"name\": \"${tests[$i]}\","
+            if is_car "${tests[$i]}"; then
+                echo '      "keywords": ["automotive_code_coverage"],'
+            fi
+
             echo "      \"host\": true"
             echo "    }$comma"
 
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index d7f4b3e..4895a1a 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",
@@ -44,46 +43,56 @@
 
         // To make sure it won't cause VerifyError (b/324063814)
         "platformprotosnano",
+
+        "com.android.internal.os.flags-aconfig-java",
     ],
     srcs: [
         "test/**/*.java",
+        "test/**/*.kt",
     ],
     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",
+        "test/**/ravenizer/*.kt",
     ],
     static_libs: [
         "junit",
         "truth",
-
-        "androidx.annotation_annotation",
-        "androidx.test.ext.junit",
-        "androidx.test.rules",
-
-        "junit-params",
-        "platform-parametric-runner-lib",
-
+        "flag-junit",
         "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/aconfig/RavenwoodAconfigFlagTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
new file mode 100644
index 0000000..fd6d6fb
--- /dev/null
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.ravenwoodtest.bivalenttest.aconfig
+
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.os.Flags
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@RunWith(AndroidJUnit4::class)
+class RavenwoodAconfigSimpleReadTests {
+    @Test
+    fun testFalseFlags() {
+        assertFalse(Flags.ravenwoodFlagRo1())
+        assertFalse(Flags.ravenwoodFlagRw1())
+    }
+
+    @Test
+    @Ignore // TODO: Enable this test after rolling out the "2" flags.
+    fun testTrueFlags() {
+        assertTrue(Flags.ravenwoodFlagRo2())
+        assertTrue(Flags.ravenwoodFlagRw2())
+    }
+}
+
+@RunWith(AndroidJUnit4::class)
+class RavenwoodAconfigCheckFlagsRuleTests {
+    @Rule
+    @JvmField
+    val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_RAVENWOOD_FLAG_RO_1)
+    fun testRequireFlagsEnabledRo() {
+        fail("This test shouldn't be executed")
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_RAVENWOOD_FLAG_RW_1)
+    fun testRequireFlagsEnabledRw() {
+        fail("This test shouldn't be executed")
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RO_2)
+    @Ignore // TODO: Enable this test after rolling out the "2" flags.
+    fun testRequireFlagsDisabledRo() {
+        fail("This test shouldn't be executed")
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RW_2)
+    @Ignore // TODO: Enable this test after rolling out the "2" flags.
+    fun testRequireFlagsDisabledRw() {
+        fail("This test shouldn't be executed")
+    }
+}
+
+@RunWith(AndroidJUnit4::class)
+class RavenwoodAconfigSetFlagsRuleWithDefaultTests {
+    @Rule
+    @JvmField
+    val setFlagsRule = SetFlagsRule()
+
+    @Test
+    @EnableFlags(Flags.FLAG_RAVENWOOD_FLAG_RO_1)
+    fun testSetRoFlag() {
+        assertTrue(Flags.ravenwoodFlagRo1())
+        assertFalse(Flags.ravenwoodFlagRw1())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_RAVENWOOD_FLAG_RW_1)
+    fun testSetRwFlag() {
+        assertFalse(Flags.ravenwoodFlagRo1())
+        assertTrue(Flags.ravenwoodFlagRw1())
+    }
+}
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..70c1d78 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -2,6 +2,9 @@
 
 com.android.internal.ravenwood.*
 
+com.android.server.FgThread
+com.android.server.ServiceThread
+
 com.android.internal.display.BrightnessSynchronizer
 com.android.internal.util.ArrayUtils
 com.android.internal.logging.MetricsLogger
@@ -359,3 +362,6 @@
 com.android.server.SystemServiceManager
 
 com.android.server.utils.TimingsTraceAndSlog
+
+android.os.IpcDataCache
+android.app.PropertyInvalidatedCache
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index a02082d..f47aaba 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -327,6 +327,10 @@
     return (this.access and Opcodes.ACC_SYNTHETIC) != 0
 }
 
+fun ClassNode.isAbstract(): Boolean {
+    return (this.access and Opcodes.ACC_ABSTRACT) != 0
+}
+
 fun MethodNode.isSynthetic(): Boolean {
     return (this.access and Opcodes.ACC_SYNTHETIC) != 0
 }
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
index 32dcbe5..a0e5599 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
@@ -46,7 +46,7 @@
     var enableValidation: SetOnce<Boolean> = SetOnce(true),
 
     /** Whether the validation failure is fatal or not. */
-    var fatalValidation: SetOnce<Boolean> = SetOnce(false),
+    var fatalValidation: SetOnce<Boolean> = SetOnce(true),
 
     /** Whether to remove mockito and dexmaker classes. */
     var stripMockito: SetOnce<Boolean> = SetOnce(false),
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/Validator.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
index 27092d2..8ec0932 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
@@ -16,10 +16,12 @@
 package com.android.platform.test.ravenwood.ravenizer
 
 import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.isAbstract
 import com.android.hoststubgen.asm.startsWithAny
 import com.android.hoststubgen.asm.toHumanReadableClassName
 import com.android.hoststubgen.log
 import org.objectweb.asm.tree.ClassNode
+import java.util.regex.Pattern
 
 fun validateClasses(classes: ClassNodes): Boolean {
     var allOk = true
@@ -41,25 +43,35 @@
     }
     var allOk = true
 
+    log.i("Checking ${cn.name.toHumanReadableClassName()}")
+
     // See if there's any class that extends a legacy base class.
     // But ignore the base classes in android.test.
-    if (!cn.name.startsWithAny("android/test/")) {
-        allOk = checkSuperClass(cn, cn, classes) && allOk
+    if (!cn.isAbstract() && !cn.name.startsWith("android/test/")
+        && !isAllowListedLegacyTest(cn)
+        ) {
+        allOk = checkSuperClassForJunit3(cn, cn, classes) && allOk
     }
     return allOk
 }
 
-fun checkSuperClass(targetClass: ClassNode, currentClass: ClassNode, classes: ClassNodes): Boolean {
+fun checkSuperClassForJunit3(
+    targetClass: ClassNode,
+    currentClass: ClassNode,
+    classes: ClassNodes,
+): Boolean {
     if (currentClass.superName == null || currentClass.superName == "java/lang/Object") {
         return true // No parent class
     }
+    // Make sure the class doesn't extend a junit3 TestCase class.
     if (currentClass.superName.isLegacyTestBaseClass()) {
         log.e("Error: Class ${targetClass.name.toHumanReadableClassName()} extends"
-                + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}.")
+                + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}"
+                + ", which is not supported on Ravenwood. Please migrate to Junit4 syntax.")
         return false
     }
     classes.findClass(currentClass.superName)?.let {
-        return checkSuperClass(targetClass, it, classes)
+        return checkSuperClassForJunit3(targetClass, it, classes)
     }
     // Super class not found.
     // log.w("Class ${currentClass.superName} not found.")
@@ -73,9 +85,64 @@
     return this.startsWithAny(
         "junit/framework/TestCase",
 
-        // In case the test doesn't statically include JUnit, we need
+        // In case the test doesn't statically include JUnit, we need the following.
         "android/test/AndroidTestCase",
         "android/test/InstrumentationTestCase",
         "android/test/InstrumentationTestSuite",
     )
 }
+
+private val allowListedLegacyTests = setOf(
+// List of existing test classes that use the JUnit3 syntax. We exempt them for now, but
+// will reject any more of them.
+//
+// Note, we want internal class names, but for convenience, we use '.'s and '%'s here
+// and replace them later. (a '$' would be parsed as a string template.)
+    *"""
+android.util.proto.cts.DebuggingTest
+android.util.proto.cts.EncodedBufferTest
+android.util.proto.cts.ProtoOutputStreamBoolTest
+android.util.proto.cts.ProtoOutputStreamBytesTest
+android.util.proto.cts.ProtoOutputStreamDoubleTest
+android.util.proto.cts.ProtoOutputStreamEnumTest
+android.util.proto.cts.ProtoOutputStreamFixed32Test
+android.util.proto.cts.ProtoOutputStreamFixed64Test
+android.util.proto.cts.ProtoOutputStreamFloatTest
+android.util.proto.cts.ProtoOutputStreamInt32Test
+android.util.proto.cts.ProtoOutputStreamInt64Test
+android.util.proto.cts.ProtoOutputStreamObjectTest
+android.util.proto.cts.ProtoOutputStreamSFixed32Test
+android.util.proto.cts.ProtoOutputStreamSFixed64Test
+android.util.proto.cts.ProtoOutputStreamSInt32Test
+android.util.proto.cts.ProtoOutputStreamSInt64Test
+android.util.proto.cts.ProtoOutputStreamStringTest
+android.util.proto.cts.ProtoOutputStreamSwitchedWriteTest
+android.util.proto.cts.ProtoOutputStreamTagTest
+android.util.proto.cts.ProtoOutputStreamUInt32Test
+android.util.proto.cts.ProtoOutputStreamUInt64Test
+
+android.os.cts.BadParcelableExceptionTest
+android.os.cts.DeadObjectExceptionTest
+android.os.cts.ParcelFormatExceptionTest
+android.os.cts.PatternMatcherTest
+android.os.cts.RemoteExceptionTest
+
+android.os.storage.StorageManagerBaseTest
+android.os.storage.StorageManagerIntegrationTest
+android.util.LogTest%PerformanceTest
+
+com.android.server.power.stats.BatteryStatsCounterTest
+com.android.server.power.stats.BatteryStatsDualTimerTest
+com.android.server.power.stats.BatteryStatsDurationTimerTest
+com.android.server.power.stats.BatteryStatsSamplingTimerTest
+com.android.server.power.stats.BatteryStatsStopwatchTimerTest
+com.android.server.power.stats.BatteryStatsTimeBaseTest
+com.android.server.power.stats.BatteryStatsTimerTest
+
+    """.trim().replace('%', '$').replace('.', '/')
+        .split(Pattern.compile("""\s+""")).toTypedArray()
+)
+
+private fun isAllowListedLegacyTest(targetClass: ClassNode): Boolean {
+    return allowListedLegacyTests.contains(targetClass.name)
+}
\ No newline at end of file
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/Android.bp b/services/Android.bp
index f04c692..899e224 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -188,6 +188,28 @@
     },
 }
 
+// Conditionally add crashrecovery stubs library
+soong_config_module_type {
+    name: "crashrecovery_java_defaults",
+    module_type: "java_defaults",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "release_crashrecovery_module",
+    ],
+    properties: [
+        "libs",
+    ],
+}
+
+crashrecovery_java_defaults {
+    name: "services_crashrecovery_stubs_conditionally",
+    soong_config_variables: {
+        release_crashrecovery_module: {
+            libs: ["service-crashrecovery.stubs.system_server"],
+        },
+    },
+}
+
 // merge all required services into one jar
 // ============================================================
 soong_config_module_type {
@@ -213,6 +235,7 @@
     defaults: [
         "services_java_defaults",
         "art_profile_java_defaults",
+        "services_crashrecovery_stubs_conditionally",
     ],
     installable: true,
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c6fe497..974cba2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -52,6 +52,7 @@
 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
 import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
@@ -3897,6 +3898,7 @@
                 userState.getShortcutTargetsLocked(HARDWARE);
         final Set<String> qsShortcutTargets =
                 userState.getShortcutTargetsLocked(QUICK_SETTINGS);
+        final Set<String> shortcutTargets = userState.getShortcutTargetsLocked(ALL);
         userState.mEnabledServices.forEach(componentName -> {
             if (packageName != null && componentName != null
                     && !packageName.equals(componentName.getPackageName())) {
@@ -3917,7 +3919,11 @@
             if (TextUtils.isEmpty(serviceName)) {
                 return;
             }
-            if (doesShortcutTargetsStringContain(buttonTargets, serviceName)
+            if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
+                if (doesShortcutTargetsStringContain(shortcutTargets, serviceName)) {
+                    return;
+                }
+            } else if (doesShortcutTargetsStringContain(buttonTargets, serviceName)
                     || doesShortcutTargetsStringContain(shortcutKeyTargets, serviceName)
                     || doesShortcutTargetsStringContain(qsShortcutTargets, serviceName)) {
                 return;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 0bf7ec00..67b4063 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -106,21 +106,17 @@
 
     final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>();
 
-    private final ArraySet<String> mAccessibilityShortcutKeyTargets = new ArraySet<>();
-
-    private final ArraySet<String> mAccessibilityButtonTargets = new ArraySet<>();
-    private final ArraySet<String> mAccessibilityGestureTargets = new ArraySet<>();
-    private final ArraySet<String> mAccessibilityQsTargets = new ArraySet<>();
+    private final HashMap<Integer, ArraySet<String>> mShortcutTargets = new HashMap<>();
 
     /**
-     * The QuickSettings tiles in the QS Panel. This can be different from
-     * {@link #mAccessibilityQsTargets} in that {@link #mA11yTilesInQsPanel} stores the
+     * The QuickSettings tiles in the QS Panel. This can be different from the QS targets in
+     * {@link #mShortcutTargets} in that {@link #mA11yTilesInQsPanel} stores the
      * TileService's or the a11y framework tile component names (e.g.
      * {@link AccessibilityShortcutController#COLOR_INVERSION_TILE_COMPONENT_NAME}) instead of the
      * A11y Feature's component names.
      * <p/>
      * In addition, {@link #mA11yTilesInQsPanel} stores what's on the QS Panel, whereas
-     * {@link #mAccessibilityQsTargets} stores the targets that configured qs as their shortcut and
+     * {@link #mShortcutTargets} stores the targets that configured qs as their shortcut and
      * also grant full device control permission.
      */
     private final ArraySet<ComponentName> mA11yTilesInQsPanel = new ArraySet<>();
@@ -208,6 +204,11 @@
         mSupportWindowMagnification = mContext.getResources().getBoolean(
                 R.bool.config_magnification_area) && mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_WINDOW_MAGNIFICATION);
+
+        mShortcutTargets.put(HARDWARE, new ArraySet<>());
+        mShortcutTargets.put(SOFTWARE, new ArraySet<>());
+        mShortcutTargets.put(GESTURE, new ArraySet<>());
+        mShortcutTargets.put(QUICK_SETTINGS, new ArraySet<>());
     }
 
     boolean isHandlingAccessibilityEventsLocked() {
@@ -233,10 +234,7 @@
         // Clear state persisted in settings.
         mEnabledServices.clear();
         mTouchExplorationGrantedServices.clear();
-        mAccessibilityShortcutKeyTargets.clear();
-        mAccessibilityButtonTargets.clear();
-        mAccessibilityGestureTargets.clear();
-        mAccessibilityQsTargets.clear();
+        mShortcutTargets.forEach((type, targets) -> targets.clear());
         mA11yTilesInQsPanel.clear();
         mTargetAssignedToAccessibilityButton = null;
         mIsTouchExplorationEnabled = false;
@@ -541,7 +539,7 @@
     private void dumpShortcutTargets(
             PrintWriter pw, @UserShortcutType int shortcutType, String name) {
         pw.append("     ").append(name).append(":{");
-        ArraySet<String> targets = getShortcutTargetsInternalLocked(shortcutType);
+        ArraySet<String> targets = getShortcutTargetsLocked(shortcutType);
         int size = targets.size();
         for (int i = 0; i < size; i++) {
             if (i > 0) {
@@ -712,7 +710,7 @@
      */
     public boolean isShortcutMagnificationEnabledLocked() {
         for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
-            if (getShortcutTargetsInternalLocked(shortcutType)
+            if (getShortcutTargetsLocked(shortcutType)
                     .contains(MAGNIFICATION_CONTROLLER_NAME)) {
                 return true;
             }
@@ -788,43 +786,29 @@
     }
 
     /**
-     * Disable both shortcuts' magnification function.
-     */
-    public void disableShortcutMagnificationLocked() {
-        mAccessibilityShortcutKeyTargets.remove(MAGNIFICATION_CONTROLLER_NAME);
-        mAccessibilityButtonTargets.remove(MAGNIFICATION_CONTROLLER_NAME);
-    }
-
-    /**
      * Returns a set which contains the flattened component names and the system class names
-     * assigned to the given shortcut. The set is a defensive copy. To apply any changes to the set,
-     * use {@link #updateShortcutTargetsLocked(Set, int)}
+     * assigned to the given shortcut. <strong>The set is a defensive copy.</strong>
+     * To apply any changes to the set, use {@link #updateShortcutTargetsLocked(Set, int)}
      *
-     * @param shortcutType The shortcut type.
+     * @param shortcutTypes The shortcut type or types (in bitmask format).
      * @return The array set of the strings
      */
-    public ArraySet<String> getShortcutTargetsLocked(@UserShortcutType int shortcutType) {
-        return new ArraySet<>(getShortcutTargetsInternalLocked(shortcutType));
-    }
-
-    private ArraySet<String> getShortcutTargetsInternalLocked(@UserShortcutType int shortcutType) {
-        if (shortcutType == HARDWARE) {
-            return mAccessibilityShortcutKeyTargets;
-        } else if (shortcutType == SOFTWARE) {
-            return mAccessibilityButtonTargets;
-        } else if (shortcutType == GESTURE) {
-            return mAccessibilityGestureTargets;
-        } else if (shortcutType == QUICK_SETTINGS) {
-            return mAccessibilityQsTargets;
-        } else if ((shortcutType == TRIPLETAP
-                && isMagnificationSingleFingerTripleTapEnabledLocked()) || (
-                shortcutType == TWOFINGER_DOUBLETAP
-                        && isMagnificationTwoFingerTripleTapEnabledLocked())) {
-            ArraySet<String> targets = new ArraySet<>();
-            targets.add(MAGNIFICATION_CONTROLLER_NAME);
-            return targets;
+    public ArraySet<String> getShortcutTargetsLocked(int shortcutTypes) {
+        ArraySet<String> targets = new ArraySet<>();
+        for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
+            if ((shortcutTypes & shortcutType) != shortcutType) {
+                continue;
+            }
+            if ((shortcutType == TRIPLETAP
+                    && isMagnificationSingleFingerTripleTapEnabledLocked()) || (
+                    shortcutType == TWOFINGER_DOUBLETAP
+                            && isMagnificationTwoFingerTripleTapEnabledLocked())) {
+                targets.add(MAGNIFICATION_CONTROLLER_NAME);
+            } else if (mShortcutTargets.containsKey(shortcutType)) {
+                targets.addAll(mShortcutTargets.get(shortcutType));
+            }
         }
-        return new ArraySet<>();
+        return targets;
     }
 
     /**
@@ -843,8 +827,10 @@
         if ((shortcutType & mask) != 0) {
             throw new IllegalArgumentException("Tap shortcuts cannot be updated with target sets.");
         }
-
-        final Set<String> currentTargets = getShortcutTargetsInternalLocked(shortcutType);
+        if (!mShortcutTargets.containsKey(shortcutType)) {
+            mShortcutTargets.put(shortcutType, new ArraySet<>());
+        }
+        ArraySet<String> currentTargets = mShortcutTargets.get(shortcutType);
         if (newTargets.equals(currentTargets)) {
             return false;
         }
@@ -904,7 +890,7 @@
         }
 
         // getting internal set lets us directly modify targets, as it's not a copy.
-        Set<String> targets = getShortcutTargetsInternalLocked(shortcutType);
+        Set<String> targets = mShortcutTargets.get(shortcutType);
         return targets.removeIf(name -> {
             ComponentName componentName;
             if (name == null
@@ -1169,13 +1155,6 @@
         );
     }
 
-    /**
-     * Returns a copy of the targets which has qs shortcut turned on
-     */
-    public ArraySet<String> getA11yQsTargets() {
-        return new ArraySet<>(mAccessibilityQsTargets);
-    }
-
     public void updateA11yTilesInQsPanelLocked(Set<ComponentName> componentNames) {
         mA11yTilesInQsPanel.clear();
         mA11yTilesInQsPanel.addAll(componentNames);
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/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 89f14b0..268e564 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -86,7 +86,6 @@
     private final Context mContext;
     private final Map<String, Object> mLocks = new WeakHashMap<>();
 
-
     public AppFunctionManagerServiceImpl(@NonNull Context context) {
         this(
                 context,
@@ -201,7 +200,7 @@
         if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
             safeExecuteAppFunctionCallback.onResult(
                     ExecuteAppFunctionResponse.newFailure(
-                            ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+                            ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
                             "Cannot run on a device with a device owner or from the managed"
                                     + " profile.",
                             /* extras= */ null));
@@ -256,7 +255,7 @@
                             if (serviceIntent == null) {
                                 safeExecuteAppFunctionCallback.onResult(
                                         ExecuteAppFunctionResponse.newFailure(
-                                                ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+                                                ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
                                                 "Cannot find the target service.",
                                                 /* extras= */ null));
                                 return;
@@ -449,7 +448,7 @@
             Slog.e(TAG, "Failed to bind to the AppFunctionService");
             safeExecuteAppFunctionCallback.onResult(
                     ExecuteAppFunctionResponse.newFailure(
-                            ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+                            ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR,
                             "Failed to bind the AppFunctionService.",
                             /* extras= */ null));
         }
@@ -464,7 +463,7 @@
         if (e instanceof CompletionException) {
             e = e.getCause();
         }
-        int resultCode = ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR;
+        int resultCode = ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR;
         if (e instanceof AppSearchException appSearchException) {
             resultCode =
                     mapAppSearchResultFailureCodeToExecuteAppFunctionResponse(
@@ -486,13 +485,13 @@
 
         switch (resultCode) {
             case AppSearchResult.RESULT_NOT_FOUND:
-                return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT;
+                return ExecuteAppFunctionResponse.RESULT_FUNCTION_NOT_FOUND;
             case AppSearchResult.RESULT_INVALID_ARGUMENT:
             case AppSearchResult.RESULT_INTERNAL_ERROR:
             case AppSearchResult.RESULT_SECURITY_ERROR:
                 // fall-through
         }
-        return ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR;
+        return ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR;
     }
 
     private void registerAppSearchObserver(@NonNull TargetUser user) {
@@ -543,12 +542,13 @@
                                     });
         }
     }
+
     /**
      * Retrieves the lock object associated with the given package name.
      *
-     * This method returns the lock object from the {@code mLocks} map if it exists.
-     * If no lock is found for the given package name, a new lock object is created,
-     * stored in the map, and returned.
+     * <p>This method returns the lock object from the {@code mLocks} map if it exists. If no lock
+     * is found for the given package name, a new lock object is created, stored in the map, and
+     * returned.
      */
     @VisibleForTesting
     @NonNull
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 35998d9a..68ff972 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -17,7 +17,6 @@
 package com.android.server.appwidget;
 
 import static android.appwidget.flags.Flags.remoteAdapterConversion;
-import static android.appwidget.flags.Flags.remoteViewsProto;
 import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
 import static android.appwidget.flags.Flags.securityPolicyInteractAcrossUsers;
 import static android.appwidget.flags.Flags.supportResumeRestoreAfterReboot;
@@ -32,7 +31,6 @@
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
 import android.Manifest;
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.PermissionName;
@@ -106,7 +104,6 @@
 import android.os.UserManager;
 import android.provider.DeviceConfig;
 import android.service.appwidget.AppWidgetServiceDumpProto;
-import android.service.appwidget.GeneratedPreviewsProto;
 import android.service.appwidget.WidgetProto;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -125,9 +122,7 @@
 import android.util.SparseLongArray;
 import android.util.TypedValue;
 import android.util.Xml;
-import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoUtils;
 import android.view.Display;
 import android.view.View;
 import android.widget.RemoteViews;
@@ -139,7 +134,6 @@
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.internal.infra.AndroidFuture;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
@@ -227,10 +221,6 @@
     // XML attribute for widget ids that are pending deletion.
     // See {@link Provider#pendingDeletedWidgetIds}.
     private static final String PENDING_DELETED_IDS_ATTR = "pending_deleted_ids";
-    // Name of service directory in /data/system_ce/<user>/
-    private static final String APPWIDGET_CE_DATA_DIRNAME = "appwidget";
-    // Name of previews directory in /data/system_ce/<user>/appwidget/
-    private static final String WIDGET_PREVIEWS_DIRNAME = "previews";
 
     // Hard limit of number of hosts an app can create, note that the app that hosts the widgets
     // can have multiple instances of {@link AppWidgetHost}, typically in respect to different
@@ -247,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) {
@@ -326,9 +320,6 @@
 
     // Handler to the background thread that saves states to disk.
     private Handler mSaveStateHandler;
-    // Handler to the background thread that saves generated previews to disk. All operations that
-    // modify saved previews must be run on this Handler.
-    private Handler mSavePreviewsHandler;
     // Handler to the foreground thread that handles broadcasts related to user
     // and package events, as well as various internal events within
     // AppWidgetService.
@@ -372,7 +363,6 @@
         } else {
             mSaveStateHandler = BackgroundThread.getHandler();
         }
-        mSavePreviewsHandler = new Handler(BackgroundThread.get().getLooper());
         final ServiceThread serviceThread = new ServiceThread(TAG,
                 android.os.Process.THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
         serviceThread.start();
@@ -392,9 +382,7 @@
                 SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_PROVIDERS,
                 DEFAULT_GENERATED_PREVIEW_MAX_PROVIDERS);
         mGeneratedPreviewsApiCounter = new ApiCounter(generatedPreviewResetInterval,
-                generatedPreviewMaxCallsPerInterval,
-                // Set a limit on the number of providers if storing them in memory.
-                remoteViewsProto() ? Integer.MAX_VALUE : generatedPreviewsMaxProviders);
+                generatedPreviewMaxCallsPerInterval, generatedPreviewsMaxProviders);
         DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI,
                 new HandlerExecutor(mCallbackHandler), this::handleSystemUiDeviceConfigChange);
 
@@ -660,14 +648,7 @@
         for (int i = 0; i < providerCount; i++) {
             Provider provider = mProviders.get(i);
             if (provider.id.uid == clearedUid) {
-                if (remoteViewsProto()) {
-                    changed |= clearGeneratedPreviewsAsync(provider);
-                } else {
-                    changed |= provider.clearGeneratedPreviewsLocked();
-                }
-                if (DEBUG) {
-                    Slog.e(TAG, "clearPreviewsForUidLocked " + provider + " changed " + changed);
-                }
+                changed |= provider.clearGeneratedPreviewsLocked();
             }
         }
         return changed;
@@ -1488,9 +1469,7 @@
         mSecurityPolicy.enforceCallFromPackage(callingPackage);
 
         // Check that if a cross-profile binding is attempted, it is allowed.
-        // Cross-profile binding is also allowed if the caller has interact across users permission.
-        if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)
-                && !mSecurityPolicy.hasCallerInteractAcrossUsersPermission()) {
+        if (!mSecurityPolicy.isEnabledGroupProfile(providerProfileId)) {
             return false;
         }
 
@@ -2459,10 +2438,8 @@
             Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId);
         }
 
-        // Ensure the profile is in the group and enabled, or that the caller has permission to
-        // interact across users.
-        if (!mSecurityPolicy.isEnabledGroupProfile(profileId)
-                && !mSecurityPolicy.hasCallerInteractAcrossUsersPermission()) {
+        // Ensure the profile is in the group and enabled.
+        if (!mSecurityPolicy.isEnabledGroupProfile(profileId)) {
             return null;
         }
 
@@ -3269,9 +3246,6 @@
         deleteWidgetsLocked(provider, UserHandle.USER_ALL);
         mProviders.remove(provider);
         mGeneratedPreviewsApiCounter.remove(provider.id);
-        if (remoteViewsProto()) {
-            clearGeneratedPreviewsAsync(provider);
-        }
 
         // no need to send the DISABLE broadcast, since the receiver is gone anyway
         cancelBroadcastsLocked(provider);
@@ -3850,14 +3824,6 @@
             } catch (IOException e) {
                 Slog.w(TAG, "Failed to read state: " + e);
             }
-
-            if (remoteViewsProto()) {
-                try {
-                    loadGeneratedPreviewCategoriesLocked(profileId);
-                } catch (IOException e) {
-                    Slog.w(TAG, "Failed to read preview categories: " + e);
-                }
-            }
         }
 
         if (version >= 0) {
@@ -4627,12 +4593,6 @@
                         keep.add(providerId);
                         // Use the new AppWidgetProviderInfo.
                         provider.setPartialInfoLocked(info);
-                        // Clear old previews
-                        if (remoteViewsProto()) {
-                            clearGeneratedPreviewsAsync(provider);
-                        } else {
-                            provider.clearGeneratedPreviewsLocked();
-                        }
                         // If it's enabled
                         final int M = provider.widgets.size();
                         if (M > 0) {
@@ -4924,7 +4884,6 @@
         mSecurityPolicy.enforceCallFromPackage(callingPackage);
         ensureWidgetCategoryCombinationIsValid(widgetCategory);
 
-        AndroidFuture<RemoteViews> result = null;
         synchronized (mLock) {
             ensureGroupStateLoadedLocked(profileId);
             final int providerCount = mProviders.size();
@@ -4958,23 +4917,10 @@
                                 callingPackage);
                 if (providerIsInCallerProfile && !shouldFilterAppAccess
                         && (providerIsInCallerPackage || hasBindAppWidgetPermission)) {
-                    if (remoteViewsProto()) {
-                        result = getGeneratedPreviewsAsync(provider, widgetCategory);
-                    } else {
-                        return provider.getGeneratedPreviewLocked(widgetCategory);
-                    }
+                    return provider.getGeneratedPreviewLocked(widgetCategory);
                 }
             }
         }
-
-        if (result != null) {
-            try {
-                return result.get();
-            } catch (Exception e) {
-                Slog.e(TAG, "Failed to get generated previews Future result", e);
-                return null;
-            }
-        }
         // Either the provider does not exist or the caller does not have permission to access its
         // previews.
         return null;
@@ -5004,12 +4950,8 @@
                         providerComponent + " is not a valid AppWidget provider");
             }
             if (mGeneratedPreviewsApiCounter.tryApiCall(providerId)) {
-                if (remoteViewsProto()) {
-                    setGeneratedPreviewsAsync(provider, widgetCategories, preview);
-                } else {
-                    provider.setGeneratedPreviewLocked(widgetCategories, preview);
-                    scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
-                }
+                provider.setGeneratedPreviewLocked(widgetCategories, preview);
+                scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
                 return true;
             }
             return false;
@@ -5037,361 +4979,11 @@
                 throw new IllegalArgumentException(
                         providerComponent + " is not a valid AppWidget provider");
             }
-
-            if (remoteViewsProto()) {
-                removeGeneratedPreviewsAsync(provider, widgetCategories);
-            } else {
-                final boolean changed = provider.removeGeneratedPreviewLocked(widgetCategories);
-                if (changed) scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
-            }
+            final boolean changed = provider.removeGeneratedPreviewLocked(widgetCategories);
+            if (changed) scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
         }
     }
 
-    /**
-     * Return previews for the specified provider from a background thread. The result of the future
-     * is nullable.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    @NonNull
-    private AndroidFuture<RemoteViews> getGeneratedPreviewsAsync(
-            @NonNull Provider provider, @AppWidgetProviderInfo.CategoryFlags int widgetCategory) {
-        AndroidFuture<RemoteViews> result = new AndroidFuture<>();
-        mSavePreviewsHandler.post(() -> {
-            SparseArray<RemoteViews> previews = loadGeneratedPreviews(provider);
-            for (int i = 0; i < previews.size(); i++) {
-                if ((widgetCategory & previews.keyAt(i)) != 0) {
-                    result.complete(previews.valueAt(i));
-                    return;
-                }
-            }
-            result.complete(null);
-        });
-        return result;
-    }
-
-    /**
-     * Set previews for the specified provider on a background thread.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    private void setGeneratedPreviewsAsync(@NonNull Provider provider, int widgetCategories,
-            @NonNull RemoteViews preview) {
-        mSavePreviewsHandler.post(() -> {
-            SparseArray<RemoteViews> previews = loadGeneratedPreviews(provider);
-            for (int flag : Provider.WIDGET_CATEGORY_FLAGS) {
-                if ((widgetCategories & flag) != 0) {
-                    previews.put(flag, preview);
-                }
-            }
-            saveGeneratedPreviews(provider, previews, /* notify= */ true);
-        });
-    }
-
-    /**
-     * Remove previews for the specified provider on a background thread.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    private void removeGeneratedPreviewsAsync(@NonNull Provider provider, int widgetCategories) {
-        mSavePreviewsHandler.post(() -> {
-            SparseArray<RemoteViews> previews = loadGeneratedPreviews(provider);
-            boolean changed = false;
-            for (int flag : Provider.WIDGET_CATEGORY_FLAGS) {
-                if ((widgetCategories & flag) != 0) {
-                    changed |= previews.removeReturnOld(flag) != null;
-                }
-            }
-            if (changed) {
-                saveGeneratedPreviews(provider, previews, /* notify= */ true);
-            }
-        });
-    }
-
-    /**
-     * Clear previews for the specified provider on a background thread. Returns true if changed
-     * (i.e. there are previews to clear). If returns true, the caller should schedule a providers
-     * changed notification.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    private boolean clearGeneratedPreviewsAsync(@NonNull Provider provider) {
-        mSavePreviewsHandler.post(() -> {
-            saveGeneratedPreviews(provider, /* previews= */ null, /* notify= */ false);
-        });
-        return provider.info.generatedPreviewCategories != 0;
-    }
-
-    private void checkSavePreviewsThread() {
-        if (DEBUG && !mSavePreviewsHandler.getLooper().isCurrentThread()) {
-            throw new IllegalStateException("Only modify previews on the background thread");
-        }
-    }
-
-    /**
-     * Load previews from file for the given provider. If there are no previews, returns an empty
-     * SparseArray. Else, returns a SparseArray of the previews mapped by widget category.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    @NonNull
-    private SparseArray<RemoteViews> loadGeneratedPreviews(@NonNull Provider provider) {
-        checkSavePreviewsThread();
-        try {
-            AtomicFile previewsFile = getWidgetPreviewsFile(provider);
-            if (!previewsFile.exists()) {
-                return new SparseArray<>();
-            }
-            ProtoInputStream input = new ProtoInputStream(previewsFile.readFully());
-            SparseArray<RemoteViews> entries = readGeneratedPreviewsFromProto(input);
-            SparseArray<RemoteViews> singleCategoryKeyedEntries = new SparseArray<>();
-            for (int i = 0; i < entries.size(); i++) {
-                int widgetCategories = entries.keyAt(i);
-                RemoteViews preview = entries.valueAt(i);
-                for (int flag : Provider.WIDGET_CATEGORY_FLAGS) {
-                    if ((widgetCategories & flag) != 0) {
-                        singleCategoryKeyedEntries.put(flag, preview);
-                    }
-                }
-            }
-            return singleCategoryKeyedEntries;
-        } catch (IOException e) {
-            Slog.e(TAG, "Failed to load generated previews for " + provider, e);
-            return new SparseArray<>();
-        }
-    }
-
-    /**
-     * This is called when loading profile/group state to populate
-     * AppWidgetProviderInfo.generatedPreviewCategories based on what previews are saved.
-     *
-     * This is the only time previews are read while not on mSavePreviewsHandler. It happens once
-     * per profile during initialization, before any calls to get/set/removeWidgetPreviewAsync
-     * happen for that profile.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    @GuardedBy("mLock")
-    private void loadGeneratedPreviewCategoriesLocked(int profileId) throws IOException {
-        for (Provider provider : mProviders) {
-            if (provider.id.getProfile().getIdentifier() != profileId) {
-                continue;
-            }
-            AtomicFile previewsFile = getWidgetPreviewsFile(provider);
-            if (!previewsFile.exists()) {
-                continue;
-            }
-            ProtoInputStream input = new ProtoInputStream(previewsFile.readFully());
-            provider.info.generatedPreviewCategories = readGeneratedPreviewCategoriesFromProto(
-                    input);
-            if (DEBUG) {
-                Slog.i(TAG, TextUtils.formatSimple(
-                        "loadGeneratedPreviewCategoriesLocked %d %s categories %d", profileId,
-                        provider, provider.info.generatedPreviewCategories));
-            }
-        }
-    }
-
-    /**
-     * Save the given previews into storage.
-     *
-     * @param provider Provider for which to save previews
-     * @param previews Previews to save. If null or empty, clears any saved previews for this
-     *                 provider.
-     * @param notify If true, then this function will notify hosts of updated provider info.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    private void saveGeneratedPreviews(@NonNull Provider provider,
-            @Nullable SparseArray<RemoteViews> previews, boolean notify) {
-        checkSavePreviewsThread();
-        AtomicFile file = null;
-        FileOutputStream stream = null;
-        try {
-            file = getWidgetPreviewsFile(provider);
-            if (previews == null || previews.size() == 0) {
-                if (file.exists()) {
-                    if (DEBUG) {
-                        Slog.i(TAG, "Deleting widget preview file " + file);
-                    }
-                    file.delete();
-                }
-            } else {
-                if (DEBUG) {
-                    Slog.i(TAG, "Writing widget preview file " + file);
-                }
-                ProtoOutputStream out = new ProtoOutputStream();
-                writePreviewsToProto(out, previews);
-                stream = file.startWrite();
-                stream.write(out.getBytes());
-                file.finishWrite(stream);
-            }
-
-            synchronized (mLock) {
-                provider.updateGeneratedPreviewCategoriesLocked(previews);
-                if (notify) {
-                    scheduleNotifyGroupHostsForProvidersChangedLocked(provider.getUserId());
-                }
-            }
-        } catch (IOException e) {
-            if (file != null && stream != null) {
-                file.failWrite(stream);
-            }
-            Slog.w(TAG, "Failed to save widget previews for provider " + provider.id.componentName);
-        }
-    }
-
-
-    /**
-     * Write the given previews as a GeneratedPreviewsProto to the output stream.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    private void writePreviewsToProto(@NonNull ProtoOutputStream out,
-            @NonNull SparseArray<RemoteViews> generatedPreviews) {
-        // Collect RemoteViews mapped by hashCode in order to avoid writing duplicates.
-        SparseArray<Pair<Integer, RemoteViews>> previewsToWrite = new SparseArray<>();
-        for (int i = 0; i < generatedPreviews.size(); i++) {
-            int widgetCategory = generatedPreviews.keyAt(i);
-            RemoteViews views = generatedPreviews.valueAt(i);
-            if (!previewsToWrite.contains(views.hashCode())) {
-                previewsToWrite.put(views.hashCode(), new Pair<>(widgetCategory, views));
-            } else {
-                Pair<Integer, RemoteViews> entry = previewsToWrite.get(views.hashCode());
-                previewsToWrite.put(views.hashCode(),
-                        Pair.create(entry.first | widgetCategory, views));
-            }
-        }
-
-        for (int i = 0; i < previewsToWrite.size(); i++) {
-            final long token = out.start(GeneratedPreviewsProto.PREVIEWS);
-            Pair<Integer, RemoteViews> entry = previewsToWrite.valueAt(i);
-            out.write(GeneratedPreviewsProto.Preview.WIDGET_CATEGORIES, entry.first);
-            final long viewsToken = out.start(GeneratedPreviewsProto.Preview.VIEWS);
-            entry.second.writePreviewToProto(mContext, out);
-            out.end(viewsToken);
-            out.end(token);
-        }
-    }
-
-    /**
-     * Read a GeneratedPreviewsProto message from the input stream.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    @NonNull
-    private SparseArray<RemoteViews> readGeneratedPreviewsFromProto(@NonNull ProtoInputStream input)
-            throws IOException {
-        SparseArray<RemoteViews> entries = new SparseArray<>();
-        while (input.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-            switch (input.getFieldNumber()) {
-                case (int) GeneratedPreviewsProto.PREVIEWS:
-                    final long token = input.start(GeneratedPreviewsProto.PREVIEWS);
-                    Pair<Integer, RemoteViews> entry = readSinglePreviewFromProto(input,
-                            /* skipViews= */ false);
-                    entries.put(entry.first, entry.second);
-                    input.end(token);
-                    break;
-                default:
-                    Slog.w(TAG, "Unknown field while reading GeneratedPreviewsProto! "
-                            + ProtoUtils.currentFieldToString(input));
-            }
-        }
-        return entries;
-    }
-
-    /**
-     * Read the widget categories from GeneratedPreviewsProto and return an int representing the
-     * combined widget categories of all the previews.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    @AppWidgetProviderInfo.CategoryFlags
-    private int readGeneratedPreviewCategoriesFromProto(@NonNull ProtoInputStream input)
-            throws IOException {
-        int widgetCategories = 0;
-        while (input.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-            switch (input.getFieldNumber()) {
-                case (int) GeneratedPreviewsProto.PREVIEWS:
-                    final long token = input.start(GeneratedPreviewsProto.PREVIEWS);
-                    Pair<Integer, RemoteViews> entry = readSinglePreviewFromProto(input,
-                            /* skipViews= */ true);
-                    widgetCategories |= entry.first;
-                    input.end(token);
-                    break;
-                default:
-                    Slog.w(TAG, "Unknown field while reading GeneratedPreviewsProto! "
-                            + ProtoUtils.currentFieldToString(input));
-            }
-        }
-        return widgetCategories;
-    }
-
-    /**
-     * Read a single GeneratedPreviewsProto.Preview message from the input stream, and returns a
-     * pair of widget category and corresponding RemoteViews. If skipViews is true, this function
-     * will only read widget categories and the returned RemoteViews will be null.
-     */
-    @FlaggedApi(android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO)
-    @NonNull
-    private Pair<Integer, RemoteViews> readSinglePreviewFromProto(@NonNull ProtoInputStream input,
-            boolean skipViews) throws IOException {
-        int widgetCategories = 0;
-        RemoteViews views = null;
-        while (input.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-            switch (input.getFieldNumber()) {
-                case (int) GeneratedPreviewsProto.Preview.VIEWS:
-                    if (skipViews)  {
-                        // ProtoInputStream will skip over the nested message when nextField() is
-                        // called.
-                        continue;
-                    }
-                    final long token = input.start(GeneratedPreviewsProto.Preview.VIEWS);
-                    try {
-                        views = RemoteViews.createPreviewFromProto(mContext, input);
-                    } catch (Exception e) {
-                        Slog.e(TAG, "Unable to deserialize RemoteViews", e);
-                    }
-                    input.end(token);
-                    break;
-                case (int) GeneratedPreviewsProto.Preview.WIDGET_CATEGORIES:
-                    widgetCategories = input.readInt(
-                            GeneratedPreviewsProto.Preview.WIDGET_CATEGORIES);
-                    break;
-                default:
-                    Slog.w(TAG, "Unknown field while reading GeneratedPreviewsProto! "
-                            + ProtoUtils.currentFieldToString(input));
-            }
-        }
-        return Pair.create(widgetCategories, views);
-    }
-
-    /**
-     * Returns the file in which all generated previews for this provider are stored. This will be
-     * a path of the form:
-     *  {@literal /data/system_ce/<userId>/appwidget/previews/<package>-<class>-<uid>.binpb}
-     *
-     * This function will not create the file if it does not already exist.
-     */
-    @NonNull
-    private static AtomicFile getWidgetPreviewsFile(@NonNull Provider provider) throws IOException {
-        int userId = provider.getUserId();
-        File previewsDirectory = getWidgetPreviewsDirectory(userId);
-        File providerPreviews = Environment.buildPath(previewsDirectory,
-                TextUtils.formatSimple("%s-%s-%d.binpb", provider.id.componentName.getPackageName(),
-                        provider.id.componentName.getClassName(), provider.id.uid));
-        return new AtomicFile(providerPreviews);
-    }
-
-    /**
-     * Returns the widget previews directory for the given user, creating it if it does not exist.
-     * This will be a path of the form:
-     *  {@literal /data/system_ce/<userId>/appwidget/previews}
-     */
-    @NonNull
-    private static File getWidgetPreviewsDirectory(int userId) throws IOException {
-        File dataSystemCeDirectory = Environment.getDataSystemCeDirectory(userId);
-        File previewsDirectory = Environment.buildPath(dataSystemCeDirectory,
-                APPWIDGET_CE_DATA_DIRNAME, WIDGET_PREVIEWS_DIRNAME);
-        if (!previewsDirectory.exists()) {
-            if (!previewsDirectory.mkdirs()) {
-                throw new IOException("Unable to create widget preview directory "
-                        + previewsDirectory.getPath());
-            }
-        }
-        return previewsDirectory;
-    }
-
     private static void ensureWidgetCategoryCombinationIsValid(int widgetCategories) {
         int validCategories = AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
                 | AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
@@ -5639,11 +5231,14 @@
                 return true;
             }
             final int userId = UserHandle.getUserId(uid);
-            if ((widget.host.getUserId() == userId || (widget.provider != null
-                    && widget.provider.getUserId() == userId))
+            if ((widget.host.getUserId() == userId
+                    || (widget.provider != null && widget.provider.getUserId() == userId)
+                    || hasCallerInteractAcrossUsersPermission())
                     && callerHasPermission(android.Manifest.permission.BIND_APPWIDGET)) {
-                // Apps that run in the same user as either the host or the provider and
-                // have the bind widget permission have access to the widget.
+                // Access to the widget requires the app to:
+                // - Run in the same user as the host or provider, or have permission to interact
+                //   across users
+                // - Have bind widget permission
                 return true;
             }
             if (DEBUG) {
@@ -5664,16 +5259,12 @@
          * The provider is accessible by the caller if any of the following is true:
          * - The provider belongs to the caller
          * - The provider belongs to a profile of the caller and is allowlisted
-         * - The caller has permission to interact across users
          */
         public boolean canAccessProvider(String packageName, int profileId) {
             final int callerId = UserHandle.getCallingUserId();
             if (profileId == callerId) {
                 return true;
             }
-            if (hasCallerInteractAcrossUsersPermission()) {
-                return true;
-            }
             final int parentId = getProfileParent(profileId);
             if (parentId != callerId) {
                 return false;
@@ -5823,11 +5414,11 @@
                                 AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
                     }
                     if (newInfo != null) {
-                        newInfo.generatedPreviewCategories = info.generatedPreviewCategories;
                         info = newInfo;
                         if (DEBUG) {
                             Objects.requireNonNull(info);
                         }
+                        updateGeneratedPreviewCategoriesLocked();
                     }
                 }
                 mInfoParsed = true;
@@ -5884,7 +5475,7 @@
                     generatedPreviews.put(flag, preview);
                 }
             }
-            updateGeneratedPreviewCategoriesLocked(generatedPreviews);
+            updateGeneratedPreviewCategoriesLocked();
         }
 
         @GuardedBy("this.mLock")
@@ -5896,7 +5487,7 @@
                 }
             }
             if (changed) {
-                updateGeneratedPreviewCategoriesLocked(generatedPreviews);
+                updateGeneratedPreviewCategoriesLocked();
             }
             return changed;
         }
@@ -5905,19 +5496,17 @@
         public boolean clearGeneratedPreviewsLocked() {
             if (generatedPreviews.size() > 0) {
                 generatedPreviews.clear();
-                updateGeneratedPreviewCategoriesLocked(generatedPreviews);
+                updateGeneratedPreviewCategoriesLocked();
                 return true;
             }
             return false;
         }
+
         @GuardedBy("this.mLock")
-        private void updateGeneratedPreviewCategoriesLocked(
-                @Nullable SparseArray<RemoteViews> previews) {
+        private void updateGeneratedPreviewCategoriesLocked() {
             info.generatedPreviewCategories = 0;
-            if (previews != null) {
-                for (int i = 0; i < previews.size(); i++) {
-                    info.generatedPreviewCategories |= previews.keyAt(i);
-                }
+            for (int i = 0; i < generatedPreviews.size(); i++) {
+                info.generatedPreviewCategories |= generatedPreviews.keyAt(i);
             }
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index c9f8929..b52c6505 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -92,6 +92,7 @@
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.infra.AbstractPerUserSystemService;
 import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.io.PrintWriter;
@@ -192,6 +193,8 @@
 
     private final ContentCaptureManagerInternal mContentCaptureManagerInternal;
 
+    private final UserManagerInternal mUserManagerInternal;
+
     private final DisabledInfoCache mDisabledInfoCache;
 
     AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
@@ -208,6 +211,7 @@
         mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
         mContentCaptureManagerInternal = LocalServices.getService(
                 ContentCaptureManagerInternal.class);
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
         mDisabledInfoCache = disableCache;
         updateLocked(disabled);
     }
@@ -379,6 +383,13 @@
             return 0;
         }
 
+        // TODO(b/376482880): remove this check once autofill service supports visible
+        // background users.
+        if (mUserManagerInternal.isVisibleBackgroundFullUser(mUserId)) {
+            Slog.d(TAG, "Currently, autofill service does not support visible background users.");
+            return 0;
+        }
+
         if (!forAugmentedAutofillOnly && isAutofillDisabledLocked(clientActivity)) {
             // Standard autofill is enabled, but service disabled autofill for this activity; that
             // means no session, unless the activity is allowlisted for augmented autofill
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/Android.bp b/services/core/Android.bp
index 348d83f..6cfd44b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -126,6 +126,7 @@
         "platform_service_defaults",
         "android.hardware.power-java_shared",
         "latest_android_hardware_broadcastradio_java_static",
+        "services_crashrecovery_stubs_conditionally",
     ],
     srcs: [
         ":android.hardware.tv.hdmi.connection-V1-java-source",
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/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 363807d..72a9a2d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -102,6 +102,7 @@
 import com.android.internal.telephony.ICarrierPrivilegesCallback;
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
 import com.android.internal.telephony.IPhoneStateListener;
+import com.android.internal.telephony.ISatelliteStateChangeListener;
 import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.flags.Flags;
@@ -126,6 +127,7 @@
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 
 /**
@@ -164,6 +166,7 @@
         IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
         ICarrierPrivilegesCallback carrierPrivilegesCallback;
         ICarrierConfigChangeListener carrierConfigChangeListener;
+        ISatelliteStateChangeListener satelliteStateChangeListener;
 
         int callerUid;
         int callerPid;
@@ -196,6 +199,10 @@
             return carrierConfigChangeListener != null;
         }
 
+        boolean matchSatelliteStateChangeListener() {
+            return satelliteStateChangeListener != null;
+        }
+
         boolean canReadCallLog() {
             try {
                 return TelephonyPermissions.checkReadCallLog(
@@ -215,6 +222,7 @@
                     + onOpportunisticSubscriptionsChangedListenerCallback
                     + " carrierPrivilegesCallback=" + carrierPrivilegesCallback
                     + " carrierConfigChangeListener=" + carrierConfigChangeListener
+                    + " satelliteStateChangeListener=" + satelliteStateChangeListener
                     + " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}";
         }
     }
@@ -433,6 +441,10 @@
 
     private List<IntArray> mCarrierRoamingNtnAvailableServices;
 
+    // Local cache to check if Satellite Modem is enabled
+    private AtomicBoolean mIsSatelliteEnabled;
+    private AtomicBoolean mWasSatelliteEnabledNotified;
+
     /**
      * Per-phone map of precise data connection state. The key of the map is the pair of transport
      * type and APN setting. This is the cache to prevent redundant callbacks to the listeners.
@@ -871,6 +883,9 @@
         mCarrierRoamingNtnMode = new boolean[numPhones];
         mCarrierRoamingNtnEligible = new boolean[numPhones];
         mCarrierRoamingNtnAvailableServices = new ArrayList<>();
+        mIsSatelliteEnabled = new AtomicBoolean();
+        mWasSatelliteEnabledNotified = new AtomicBoolean();
+
 
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
@@ -3425,6 +3440,94 @@
     }
 
     @Override
+    public void addSatelliteStateChangeListener(@NonNull ISatelliteStateChangeListener listener,
+            @NonNull String pkg, @Nullable String featureId) {
+        final int callerUserId = UserHandle.getCallingUserId();
+        mAppOps.checkPackage(Binder.getCallingUid(), pkg);
+        enforceCallingOrSelfAtLeastReadBasicPhoneStatePermission(pkg, featureId,
+                "addSatelliteStateChangeListener");
+        if (VDBG) {
+            log("addSatelliteStateChangeListener pkg=" + pii(pkg)
+                    + " uid=" + Binder.getCallingUid()
+                    + " myUserId=" + UserHandle.myUserId() + " callerUerId" + callerUserId
+                    + " listener=" + listener + " listener.asBinder=" + listener.asBinder());
+        }
+
+        synchronized (mRecords) {
+            final IBinder b = listener.asBinder();
+            boolean doesLimitApply = doesLimitApplyForListeners(Binder.getCallingUid(),
+                    Process.myUid());
+            Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply);
+
+            if (r == null) {
+                loge("addSatelliteStateChangeListener: can not create Record instance!");
+                return;
+            }
+
+            r.context = mContext;
+            r.satelliteStateChangeListener = listener;
+            r.callingPackage = pkg;
+            r.callingFeatureId = featureId;
+            r.callerUid = Binder.getCallingUid();
+            r.callerPid = Binder.getCallingPid();
+            r.eventList = new ArraySet<>();
+            if (DBG) {
+                log("addSatelliteStateChangeListener:  Register r=" + r);
+            }
+
+            // Always notify registrants on registration if it has been notified before
+            if (mWasSatelliteEnabledNotified.get() && r.matchSatelliteStateChangeListener()) {
+                try {
+                    r.satelliteStateChangeListener.onSatelliteEnabledStateChanged(
+                            mIsSatelliteEnabled.get());
+                } catch (RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void removeSatelliteStateChangeListener(@NonNull ISatelliteStateChangeListener listener,
+            @NonNull String pkg) {
+        if (DBG) log("removeSatelliteStateChangeListener listener=" + listener + ", pkg=" + pkg);
+        mAppOps.checkPackage(Binder.getCallingUid(), pkg);
+        enforceCallingOrSelfAtLeastReadBasicPhoneStatePermission(pkg, null,
+                "removeSatelliteStateChangeListener");
+        remove(listener.asBinder());
+    }
+
+    @Override
+    public void notifySatelliteStateChanged(boolean isEnabled) {
+        if (!checkNotifyPermission("notifySatelliteStateChanged")) {
+            loge("notifySatelliteStateChanged: Caller has no notify permission!");
+            return;
+        }
+        if (VDBG) {
+            log("notifySatelliteStateChanged: isEnabled=" + isEnabled);
+        }
+
+        mWasSatelliteEnabledNotified.set(true);
+        mIsSatelliteEnabled.set(isEnabled);
+
+        synchronized (mRecords) {
+            mRemoveList.clear();
+            for (Record r : mRecords) {
+                // Listeners are "global", neither per-slot nor per-sub, so no idMatch check here
+                if (!r.matchSatelliteStateChangeListener()) {
+                    continue;
+                }
+                try {
+                    r.satelliteStateChangeListener.onSatelliteEnabledStateChanged(isEnabled);
+                } catch (RemoteException re) {
+                    mRemoveList.add(r.binder);
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
+    @Override
     public void notifyMediaQualityStatusChanged(int phoneId, int subId, MediaQualityStatus status) {
         if (!checkNotifyPermission("notifyMediaQualityStatusChanged()")) {
             return;
@@ -4622,4 +4725,32 @@
         if (packageNames.isEmpty() || Build.IS_DEBUGGABLE) return packageNames.toString();
         return "[***, size=" + packageNames.size() + "]";
     }
+
+    /**
+     * The method enforces the calling package at least has READ_BASIC_PHONE_STATE permission.
+     * That is, calling package either has READ_PRIVILEGED_PHONE_STATE, READ_PHONE_STATE or Carrier
+     * Privileges on ANY active subscription, or has READ_BASIC_PHONE_STATE permission.
+     */
+    private void enforceCallingOrSelfAtLeastReadBasicPhoneStatePermission(String pkgName,
+            String featureId, String message) {
+        // Check if calling app has READ_PHONE_STATE on ANY active subscription
+        boolean hasReadPhoneState = false;
+        SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+        if (sm != null) {
+            for (int subId : sm.getActiveSubscriptionIdList()) {
+                if (TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow(mContext, subId,
+                        pkgName, featureId, message)) {
+                    hasReadPhoneState = true;
+                    break;
+                }
+            }
+        }
+
+        // If yes, pass. If not, then enforce READ_BASIC_PHONE_STATE permission
+        if (!hasReadPhoneState) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.READ_BASIC_PHONE_STATE,
+                    message);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 51c768b..06e6c8b 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -48,6 +48,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.net.vcn.Flags;
 import android.net.vcn.IVcnManagementService;
 import android.net.vcn.IVcnStatusCallback;
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
@@ -524,6 +525,9 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
+            if (action == null) {
+                return;
+            }
 
             switch (action) {
                 case Intent.ACTION_PACKAGE_ADDED: // Fallthrough
@@ -878,6 +882,7 @@
 
     private void garbageCollectAndWriteVcnConfigsLocked() {
         final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class);
+        final Set<ParcelUuid> subGroups = mLastSnapshot.getAllSubscriptionGroups();
 
         boolean shouldWrite = false;
 
@@ -885,11 +890,20 @@
         while (configsIterator.hasNext()) {
             final ParcelUuid subGrp = configsIterator.next();
 
-            final List<SubscriptionInfo> subscriptions = subMgr.getSubscriptionsInGroup(subGrp);
-            if (subscriptions == null || subscriptions.isEmpty()) {
-                // Trim subGrps with no more subscriptions; must have moved to another subGrp
-                configsIterator.remove();
-                shouldWrite = true;
+            if (Flags.fixConfigGarbageCollection()) {
+                if (!subGroups.contains(subGrp)) {
+                    // Trim subGrps with no more subscriptions; must have moved to another subGrp
+                    logDbg("Garbage collect VcnConfig for group=" + subGrp);
+                    configsIterator.remove();
+                    shouldWrite = true;
+                }
+            } else {
+                final List<SubscriptionInfo> subscriptions = subMgr.getSubscriptionsInGroup(subGrp);
+                if (subscriptions == null || subscriptions.isEmpty()) {
+                    // Trim subGrps with no more subscriptions; must have moved to another subGrp
+                    configsIterator.remove();
+                    shouldWrite = true;
+                }
             }
         }
 
@@ -1094,13 +1108,7 @@
             synchronized (mLock) {
                 final Vcn vcn = mVcns.get(subGrp);
                 final VcnConfig vcnConfig = mConfigs.get(subGrp);
-                if (vcn != null) {
-                    if (vcnConfig == null) {
-                        // TODO: b/284381334 Investigate for the root cause of this issue
-                        // and handle it properly
-                        logWtf("Vcn instance exists but VcnConfig does not for " + subGrp);
-                    }
-
+                if (vcn != null && vcnConfig != null) {
                     if (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE) {
                         isVcnManagedNetwork = true;
                     }
@@ -1120,6 +1128,8 @@
                             }
                         }
                     }
+                } else if (vcn != null && vcnConfig == null) {
+                    logWtf("Vcn instance exists but VcnConfig does not for " + subGrp);
                 }
             }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2a9cb43..1c3569d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -60,7 +60,6 @@
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_INSTRUMENTATION;
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_PERSISTENT;
 import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_SYSTEM;
-import static android.content.Intent.isPreventIntentRedirectEnabled;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
 import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
 import static android.content.pm.PackageManager.MATCH_ALL;
@@ -131,6 +130,7 @@
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
+import static android.security.Flags.preventIntentRedirect;
 import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -14301,6 +14301,10 @@
         mBroadcastController.unregisterReceiver(receiver);
     }
 
+    public List<IntentFilter> getRegisteredIntentFilters(IIntentReceiver receiver) {
+        return mBroadcastController.getRegisteredIntentFilters(receiver);
+    }
+
     @GuardedBy("this")
     final int broadcastIntentLocked(ProcessRecord callerApp,
             String callerPackage, String callerFeatureId, Intent intent, String resolvedType,
@@ -19281,7 +19285,7 @@
      * @hide
      */
     public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
-        if (!isPreventIntentRedirectEnabled()) return;
+        if (!preventIntentRedirect()) return;
 
         if (intent == null || intent.getExtraIntentKeys() == null) return;
         for (String key : intent.getExtraIntentKeys()) {
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..b0f88071 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
@@ -679,6 +679,21 @@
         }
     }
 
+    List<IntentFilter> getRegisteredIntentFilters(IIntentReceiver receiver) {
+        synchronized (mService) {
+            final ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
+            if (rl == null) {
+                return null;
+            }
+            final ArrayList<IntentFilter> filters = new ArrayList<>();
+            final int count = rl.size();
+            for (int i = 0; i < count; ++i) {
+                filters.add(rl.get(i));
+            }
+            return filters;
+        }
+    }
+
     int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle resultExtras,
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index adb2392..3c7fb52 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -17,16 +17,31 @@
 package com.android.server.am;
 
 import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 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)
+    @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 +53,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 +70,8 @@
         instantApp = _instantApp;
         visibleToInstantApp = _visibleToInstantApp;
         exported = _exported;
+        initialPriority = getPriority();
+        setPriority(calculateAdjustedPriority(owningUid, initialPriority, platformCompat));
     }
 
     public @Nullable String getReceiverClassName() {
@@ -100,6 +118,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/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 61079fc..c1d5597 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -49,6 +49,7 @@
 
 # Broadcasts
 per-file Broadcast* = file:/BROADCASTS_OWNERS
+per-file broadcasts_flags.aconfig = file:/BROADCASTS_OWNERS
 
 # Permissions & Packages
 per-file *Permission* = patb@google.com
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 42966a3..de3e2c9 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -4068,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,
@@ -4077,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")
@@ -4100,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")
@@ -4118,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")
@@ -4133,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 4331e05..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) {
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 69102f1..8dc7c73 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -41,6 +41,7 @@
 import android.aconfigd.Aconfigd.StorageReturnMessages;
 import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
 import static com.android.aconfig_new_storage.Flags.supportImmediateLocalOverrides;
+import static com.android.aconfig_new_storage.Flags.supportClearLocalOverridesImmediately;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -176,6 +177,7 @@
         "core_libraries",
         "crumpet",
         "dck_framework",
+        "desktop_hwsec",
         "desktop_stats",
         "devoptions_settings",
         "game",
@@ -208,6 +210,7 @@
         "pixel_perf",
         "pixel_sensors",
         "pixel_system_sw_video",
+        "pixel_video_sw",
         "pixel_watch",
         "platform_compat",
         "platform_security",
@@ -234,6 +237,7 @@
         "text",
         "threadnetwork",
         "treble",
+        "tv_os_media",
         "tv_system_ui",
         "usb",
         "vibrator",
@@ -523,14 +527,21 @@
      * @param proto
      * @param packageName the package of the flag
      * @param flagName the name of the flag
+     * @param immediate if true, clear immediately; otherwise, clear on next reboot
+     *
+     * @hide
      */
-    static void writeFlagOverrideRemovalRequest(
-        ProtoOutputStream proto, String packageName, String flagName) {
+    public static void writeFlagOverrideRemovalRequest(
+        ProtoOutputStream proto, String packageName, String flagName, boolean immediate) {
       long msgsToken = proto.start(StorageRequestMessages.MSGS);
       long msgToken = proto.start(StorageRequestMessage.REMOVE_LOCAL_OVERRIDE_MESSAGE);
       proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.PACKAGE_NAME, packageName);
       proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.FLAG_NAME, flagName);
       proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.REMOVE_ALL, false);
+      proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.REMOVE_OVERRIDE_TYPE,
+          immediate
+              ? StorageRequestMessage.REMOVE_LOCAL_IMMEDIATE
+              : StorageRequestMessage.REMOVE_LOCAL_ON_REBOOT);
       proto.end(msgToken);
       proto.end(msgsToken);
     }
@@ -607,7 +618,11 @@
             String realFlagName = fullFlagName.substring(idx+1);
 
             if (Flags.syncLocalOverridesRemovalNewStorage() && flagValue == null) {
-              writeFlagOverrideRemovalRequest(requests, packageName, realFlagName);
+              if (supportClearLocalOverridesImmediately()) {
+                writeFlagOverrideRemovalRequest(requests, packageName, realFlagName, true);
+              } else {
+                writeFlagOverrideRemovalRequest(requests, packageName, realFlagName, false);
+              }
             } else {
               writeFlagOverrideRequest(requests, packageName, realFlagName, flagValue, true);
             }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 31ae966..c31b9ef 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2383,7 +2383,7 @@
             // If running in background is disabled or mStopUserOnSwitch mode, stop the user.
             if (hasRestriction || isStopUserOnSwitchEnabled()) {
                 Slogf.i(TAG, "Stopping user %d and its profiles on user switch", oldUserId);
-                stopUsersLU(oldUserId, /* allowDelayedLocking= */ false, null, null);
+                stopUsersLU(oldUserId, /* allowDelayedLocking= */ !hasRestriction, null, null);
                 return;
             }
         }
diff --git a/services/core/java/com/android/server/am/broadcasts_flags.aconfig b/services/core/java/com/android/server/am/broadcasts_flags.aconfig
new file mode 100644
index 0000000..b1185d5
--- /dev/null
+++ b/services/core/java/com/android/server/am/broadcasts_flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.server.am"
+container: "system"
+
+flag {
+    name: "restrict_priority_values"
+    namespace: "backstage_power"
+    description: "Restrict priority values defined by non-system apps"
+    is_fixed_read_only: true
+    bug: "369487976"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index d67fea0..56cfdfb 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -233,3 +233,13 @@
     description: "Assign cached oom_score_adj in tiers."
     bug: "369893532"
 }
+
+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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index b4cce7d..702ad95 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -70,8 +70,10 @@
 import static android.content.Intent.EXTRA_REPLACING;
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
 import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
-import static android.permission.flags.Flags.deviceAwareAppOpNewSchemaEnabled;
+import static android.os.Flags.binderFrozenStateChangeCallback;
 import static android.permission.flags.Flags.checkOpValidatePackage;
+import static android.permission.flags.Flags.deviceAwareAppOpNewSchemaEnabled;
+import static android.permission.flags.Flags.useFrozenAwareRemoteCallbackList;
 
 import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED;
 import static com.android.internal.util.FrameworkStatsLog.APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION;
@@ -182,6 +184,7 @@
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.policy.AppOpsPolicy;
+import com.android.server.selinux.RateLimiter;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -201,6 +204,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
+import java.time.Duration;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
@@ -351,6 +355,10 @@
     @GuardedBy("this")
     private boolean mUidStatesInitialized;
 
+    // A rate limiter to prevent excessive Atom pushing. Used by noteOperation.
+    private static final Duration RATE_LIMITER_WINDOW = Duration.ofMillis(10);
+    private final RateLimiter mRateLimiter = new RateLimiter(RATE_LIMITER_WINDOW);
+
     volatile @NonNull HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this);
 
     /*
@@ -3135,10 +3143,12 @@
             boolean shouldCollectMessage) {
         if (Binder.getCallingPid() != Process.myPid()
                 && Flags.appopAccessTrackingLoggingEnabled()) {
-            FrameworkStatsLog.write(
-                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code,
-                    APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_OPERATION,
-                    attributionTag != null);
+            if (mRateLimiter.tryAcquire()) {
+                FrameworkStatsLog.write(
+                        APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED, uid, code,
+                        APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__NOTE_OPERATION,
+                        attributionTag != null);
+            }
         }
         return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
                 attributionTag, Context.DEVICE_ID_DEFAULT, shouldCollectAsyncNotedOp, message,
@@ -3543,20 +3553,23 @@
 
         synchronized (this) {
             RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
+            if (callbacks == null && binderFrozenStateChangeCallback()
+                    && useFrozenAwareRemoteCallbackList()) {
+                callbacks = new RemoteCallbackList.Builder<IAppOpsAsyncNotedCallback>(
+                        RemoteCallbackList.FROZEN_CALLEE_POLICY_DROP)
+                        .setInterfaceDiedCallback((rcl, cb, cookie) ->
+                            stopWatchingAsyncNoted(packageName, callback)
+                        ).build();
+            }
             if (callbacks == null) {
                 callbacks = new RemoteCallbackList<IAppOpsAsyncNotedCallback>() {
-                    @Override
-                    public void onCallbackDied(IAppOpsAsyncNotedCallback callback) {
-                        synchronized (AppOpsService.this) {
-                            if (getRegisteredCallbackCount() == 0) {
-                                mAsyncOpWatchers.remove(key);
-                            }
+                        @Override
+                        public void onCallbackDied(IAppOpsAsyncNotedCallback cb) {
+                            stopWatchingAsyncNoted(packageName, callback);
                         }
-                    }
-                };
-                mAsyncOpWatchers.put(key, callbacks);
+                    };
             }
-
+            mAsyncOpWatchers.put(key, callbacks);
             callbacks.register(callback);
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 906e584..a3b20b9 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -693,6 +693,8 @@
                 elapsed = System.currentTimeMillis() - start;
                 if (elapsed >= SET_COMMUNICATION_DEVICE_TIMEOUT_MS) {
                     Log.e(TAG, "Timeout waiting for communication device update.");
+                    // reset counter to avoid sticky out of sync condition
+                    mCommunicationDeviceUpdateCount = 0;
                     break;
                 }
             }
@@ -1342,8 +1344,8 @@
     }
 
     /*package*/ void postSetModeOwner(int mode, int pid, int uid, boolean signal) {
-        sendILMsgNoDelay(MSG_IL_SET_MODE_OWNER, SENDMSG_REPLACE,
-                signal ? 1 : 0, new AudioModeInfo(mode, pid, uid));
+        sendLMsgNoDelay(signal ? MSG_L_SET_MODE_OWNER_SIGNAL : MSG_L_SET_MODE_OWNER,
+                SENDMSG_REPLACE, new AudioModeInfo(mode, pid, uid));
     }
 
     /*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) {
@@ -2026,7 +2028,8 @@
                         mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
                     }
                     break;
-                case MSG_IL_SET_MODE_OWNER:
+                case MSG_L_SET_MODE_OWNER:
+                case MSG_L_SET_MODE_OWNER_SIGNAL:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
                             mAudioModeOwner = (AudioModeInfo) msg.obj;
@@ -2037,7 +2040,7 @@
                             }
                         }
                     }
-                    if (msg.arg1 == 1 /*signal*/) {
+                    if (msg.what == MSG_L_SET_MODE_OWNER_SIGNAL) {
                         mAudioService.decrementAudioModeResetCount();
                     }
                     break;
@@ -2199,7 +2202,8 @@
     private static final int MSG_REPORT_NEW_ROUTES = 13;
     private static final int MSG_II_SET_HEARING_AID_VOLUME = 14;
     private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15;
-    private static final int MSG_IL_SET_MODE_OWNER = 16;
+    private static final int MSG_L_SET_MODE_OWNER = 16;
+    private static final int MSG_L_SET_MODE_OWNER_SIGNAL = 17;
 
     private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22;
     private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 353cfbe..985155d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -457,7 +457,7 @@
     private static final int MSG_UPDATE_AUDIO_MODE = 36;
     private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
     private static final int MSG_BT_DEV_CHANGED = 38;
-
+    private static final int MSG_UPDATE_AUDIO_MODE_SIGNAL = 39;
     private static final int MSG_DISPATCH_AUDIO_MODE = 40;
     private static final int MSG_ROUTING_UPDATED = 41;
     private static final int MSG_INIT_HEADTRACKING_SENSORS = 42;
@@ -2070,22 +2070,6 @@
 
         onIndicateSystemReady();
 
-        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);
-                }
-            });
-        }
-
         // indicate the end of reconfiguration phase to audio HAL
         AudioSystem.setParameters("restarting=false");
 
@@ -2200,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;
@@ -4804,16 +4804,14 @@
     }
 
     static class UpdateAudioModeInfo {
-        UpdateAudioModeInfo(int mode, int pid, String packageName, boolean signal) {
+        UpdateAudioModeInfo(int mode, int pid, String packageName) {
             mMode = mode;
             mPid = pid;
             mPackageName = packageName;
-            mSignal = signal;
         }
         private final int mMode;
         private final int mPid;
         private final String mPackageName;
-        private final boolean mSignal;
 
         int getMode() {
             return mMode;
@@ -4824,9 +4822,6 @@
         String getPackageName() {
             return mPackageName;
         }
-        boolean getSignal() {
-            return mSignal;
-        }
     }
 
     void postUpdateAudioMode(int msgPolicy, int mode, int pid, String packageName,
@@ -4835,8 +4830,8 @@
             if (signal) {
                 mAudioModeResetCount++;
             }
-            sendMsg(mAudioHandler, MSG_UPDATE_AUDIO_MODE, msgPolicy, 0, 0,
-                new UpdateAudioModeInfo(mode, pid, packageName, signal), delay);
+            sendMsg(mAudioHandler, signal ? MSG_UPDATE_AUDIO_MODE_SIGNAL : MSG_UPDATE_AUDIO_MODE,
+                    msgPolicy, 0, 0, new UpdateAudioModeInfo(mode, pid, packageName), delay);
         }
     }
 
@@ -6654,6 +6649,9 @@
                 // connections not started by the application changing the mode when pid changes
                 mDeviceBroker.postSetModeOwner(mode, pid, uid, signal);
             } else {
+                // reset here to avoid sticky out of sync condition (would have been reset
+                // by AudioDeviceBroker processing MSG_L_SET_MODE_OWNER_SIGNAL message)
+                resetAudioModeResetCount();
                 Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode);
             }
         }
@@ -10420,10 +10418,11 @@
                     break;
 
                 case MSG_UPDATE_AUDIO_MODE:
+                case MSG_UPDATE_AUDIO_MODE_SIGNAL:
                     synchronized (mDeviceBroker.mSetModeLock) {
                         UpdateAudioModeInfo info = (UpdateAudioModeInfo) msg.obj;
                         onUpdateAudioMode(info.getMode(), info.getPid(), info.getPackageName(),
-                                false /*force*/, info.getSignal());
+                                false /*force*/, msg.what == MSG_UPDATE_AUDIO_MODE_SIGNAL);
                     }
                     break;
 
@@ -11164,6 +11163,8 @@
                     elapsed = java.lang.System.currentTimeMillis() - start;
                     if (elapsed >= AUDIO_MODE_RESET_TIMEOUT_MS) {
                         Log.e(TAG, "Timeout waiting for audio mode reset");
+                        // reset count to avoid sticky out of sync state.
+                        resetAudioModeResetCount();
                         break;
                     }
                 }
@@ -11175,7 +11176,7 @@
         return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
     }
 
-    /** synchronization between setMode(NORMAL) and abandonAudioFocus() frmo Telecom */
+    /** synchronization between setMode(NORMAL) and abandonAudioFocus() from Telecom */
     private static final long AUDIO_MODE_RESET_TIMEOUT_MS = 3000;
 
     private final Object mAudioModeResetLock = new Object();
@@ -11194,6 +11195,13 @@
         }
     }
 
+    private void resetAudioModeResetCount() {
+        synchronized (mAudioModeResetLock) {
+            mAudioModeResetCount = 0;
+            mAudioModeResetLock.notify();
+        }
+    }
+
     /** see {@link AudioManager#abandonAudioFocusForTest(AudioFocusRequest, String)} */
     public int abandonAudioFocusForTest(IAudioFocusDispatcher fd, String clientId,
             AudioAttributes aa, String callingPackageName) {
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/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 3afecf1..290aab2 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -305,7 +305,7 @@
                     mSensors /* sensorIds */,
                     true /* credentialAllowed */,
                     false /* requireConfirmation */,
-                    mUserId,
+                    mPreAuthInfo.callingUserId,
                     mOperationId,
                     mOpPackageName,
                     mRequestId);
@@ -357,7 +357,7 @@
                             mSensors,
                             mPreAuthInfo.shouldShowCredential(),
                             requireConfirmation,
-                            mUserId,
+                            mPreAuthInfo.callingUserId,
                             mOperationId,
                             mOpPackageName,
                             mRequestId);
@@ -491,7 +491,7 @@
                             mSensors /* sensorIds */,
                             true /* credentialAllowed */,
                             false /* requireConfirmation */,
-                            mUserId,
+                            mPreAuthInfo.callingUserId,
                             mOperationId,
                             mOpPackageName,
                             mRequestId);
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 4c91789..00280c8f 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1133,7 +1133,7 @@
 
         return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors,
                 userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */,
-                getContext(), mBiometricCameraManager);
+                getContext(), mBiometricCameraManager, mUserManager);
     }
 
     /**
@@ -1520,9 +1520,9 @@
         mHandler.post(() -> {
             try {
                 final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager,
-                        mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo,
-                        opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(),
-                        getContext(), mBiometricCameraManager);
+                        mDevicePolicyManager, mSettingObserver, mSensors, userId,
+                        promptInfo, opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(),
+                        getContext(), mBiometricCameraManager, mUserManager);
 
                 // Set the default title if necessary.
                 if (promptInfo.isUseDefaultTitle()) {
@@ -1572,8 +1572,8 @@
                         promptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL);
                     }
 
-                    authenticateInternal(token, requestId, operationId, userId, receiver,
-                            opPackageName, promptInfo, preAuthInfo);
+                    authenticateInternal(token, requestId, operationId, preAuthInfo.userId,
+                            receiver, opPackageName, promptInfo, preAuthInfo);
                 } else {
                     receiver.onError(preAuthStatus.first /* modality */,
                             preAuthStatus.second /* errorCode */,
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 96c178a..e8fa417 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -32,6 +32,7 @@
 import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.PromptInfo;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.util.Pair;
 import android.util.Slog;
 
@@ -72,6 +73,7 @@
     final boolean confirmationRequested;
     final boolean ignoreEnrollmentState;
     final int userId;
+    final int callingUserId;
     final Context context;
     private final boolean mBiometricRequested;
     private final int mBiometricStrengthRequested;
@@ -82,7 +84,7 @@
     private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
             boolean credentialRequested, List<BiometricSensor> eligibleSensors,
             List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
-            PromptInfo promptInfo, int userId, Context context,
+            PromptInfo promptInfo, int userId, int callingUserId, Context context,
             BiometricCameraManager biometricCameraManager,
             boolean isOnlyMandatoryBiometricsRequested,
             boolean isMandatoryBiometricsAuthentication) {
@@ -97,6 +99,7 @@
         this.confirmationRequested = promptInfo.isConfirmationRequested();
         this.ignoreEnrollmentState = promptInfo.isIgnoreEnrollmentState();
         this.userId = userId;
+        this.callingUserId = callingUserId;
         this.context = context;
         this.mOnlyMandatoryBiometricsRequested = isOnlyMandatoryBiometricsRequested;
         this.mIsMandatoryBiometricsAuthentication = isMandatoryBiometricsAuthentication;
@@ -108,7 +111,8 @@
             List<BiometricSensor> sensors,
             int userId, PromptInfo promptInfo, String opPackageName,
             boolean checkDevicePolicyManager, Context context,
-            BiometricCameraManager biometricCameraManager)
+            BiometricCameraManager biometricCameraManager,
+            UserManager userManager)
             throws RemoteException {
 
         final boolean isOnlyMandatoryBiometricsRequested = promptInfo.getAuthenticators()
@@ -141,14 +145,20 @@
         final List<BiometricSensor> eligibleSensors = new ArrayList<>();
         final List<Pair<BiometricSensor, Integer>> ineligibleSensors = new ArrayList<>();
 
+        final int effectiveUserId;
+        if (Flags.effectiveUserBp()) {
+            effectiveUserId = userManager.getCredentialOwnerProfile(userId);
+        } else {
+            effectiveUserId = userId;
+        }
+
         if (biometricRequested) {
             for (BiometricSensor sensor : sensors) {
 
                 @AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
-                        devicePolicyManager, settingObserver, sensor, userId, opPackageName,
-                        checkDevicePolicyManager, requestedStrength,
-                        promptInfo.getAllowedSensorIds(),
-                        promptInfo.isIgnoreEnrollmentState(),
+                        devicePolicyManager, settingObserver, sensor, effectiveUserId,
+                        opPackageName, checkDevicePolicyManager, requestedStrength,
+                        promptInfo.getAllowedSensorIds(), promptInfo.isIgnoreEnrollmentState(),
                         biometricCameraManager);
 
                 Slog.d(TAG, "Package: " + opPackageName
@@ -172,9 +182,9 @@
         }
 
         return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
-                eligibleSensors, ineligibleSensors, credentialAvailable, promptInfo, userId,
-                context, biometricCameraManager, isOnlyMandatoryBiometricsRequested,
-                isMandatoryBiometricsAuthentication);
+                eligibleSensors, ineligibleSensors, credentialAvailable, promptInfo,
+                effectiveUserId, userId, context, biometricCameraManager,
+                isOnlyMandatoryBiometricsRequested, isMandatoryBiometricsAuthentication);
     }
 
     private static boolean dropCredentialFallback(int authenticators,
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/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index a3c68f9..afffa66 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -21,6 +21,7 @@
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
+import android.hardware.broadcastradio.Alert;
 import android.hardware.broadcastradio.AmFmRegionConfig;
 import android.hardware.broadcastradio.Announcement;
 import android.hardware.broadcastradio.ConfigFlag;
@@ -36,6 +37,7 @@
 import android.hardware.radio.Flags;
 import android.hardware.radio.ProgramList;
 import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioAlert;
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioMetadata;
 import android.hardware.radio.RadioTuner;
@@ -573,6 +575,86 @@
         return builder.build();
     }
 
+    @Nullable private static RadioAlert.Polygon polygonFromHalPolygon(
+            android.hardware.broadcastradio.Polygon halPolygon) {
+        if (halPolygon.coordinates.length < 4) {
+            Slogf.e(TAG, "Number of coordinates in alert polygon cannot be less than 4");
+            return null;
+        } else if (halPolygon.coordinates[0].latitude
+                != halPolygon.coordinates[halPolygon.coordinates.length - 1].latitude
+                || halPolygon.coordinates[0].longitude
+                != halPolygon.coordinates[halPolygon.coordinates.length - 1].longitude) {
+            Slogf.e(TAG, "The first and the last coordinate in alert polygon cannot be different");
+            return null;
+        }
+        List<RadioAlert.Coordinate> coordinates = new ArrayList<>(halPolygon.coordinates.length);
+        for (int idx = 0; idx < halPolygon.coordinates.length; idx++) {
+            coordinates.add(new RadioAlert.Coordinate(halPolygon.coordinates[idx].latitude,
+                    halPolygon.coordinates[idx].longitude));
+        }
+        return new RadioAlert.Polygon(coordinates);
+    }
+
+    private static RadioAlert.Geocode geocodeFromHalGeocode(
+            android.hardware.broadcastradio.Geocode geocode) {
+        return new RadioAlert.Geocode(geocode.valueName, geocode.value);
+    }
+
+    private static RadioAlert.AlertArea alertAreaFromHalAlertArea(
+            android.hardware.broadcastradio.AlertArea halAlertArea) {
+        List<RadioAlert.Polygon> polygonList = new ArrayList<>();
+        for (int idx = 0; idx < halAlertArea.polygons.length; idx++) {
+            RadioAlert.Polygon polygon = polygonFromHalPolygon(halAlertArea.polygons[idx]);
+            if (polygon != null) {
+                polygonList.add(polygon);
+            }
+        }
+        List<RadioAlert.Geocode> geocodeList = new ArrayList<>(halAlertArea.geocodes.length);
+        for (int idx = 0; idx < halAlertArea.geocodes.length; idx++) {
+            geocodeList.add(geocodeFromHalGeocode(halAlertArea.geocodes[idx]));
+        }
+        return new RadioAlert.AlertArea(polygonList, geocodeList);
+    }
+
+    private static RadioAlert.AlertInfo alertInfoFromHalAlertInfo(
+            android.hardware.broadcastradio.AlertInfo halAlertInfo) {
+        int[] categoryArray = new int[halAlertInfo.categoryArray.length];
+        for (int idx = 0; idx < halAlertInfo.categoryArray.length; idx++) {
+            // Integer values in android.hardware.radio.RadioAlert.AlertCategory and
+            // android.hardware.broadcastradio.AlertCategory match.
+            categoryArray[idx] = halAlertInfo.categoryArray[idx];
+        }
+        List<RadioAlert.AlertArea> alertAreaList = new ArrayList<>();
+        for (int idx = 0; idx < halAlertInfo.areas.length; idx++) {
+            alertAreaList.add(alertAreaFromHalAlertArea(halAlertInfo.areas[idx]));
+        }
+        // Integer values in android.hardware.radio.RadioAlert.AlertUrgency and
+        // android.hardware.broadcastradio.AlertUrgency match.
+        // Integer values in android.hardware.radio.RadioAlert.AlertSeverity and
+        // android.hardware.broadcastradio.AlertSeverity match.
+        // Integer values in android.hardware.radio.RadioAlert.AlertCertainty and
+        // android.hardware.broadcastradio.AlertCertainty match.
+        return new RadioAlert.AlertInfo(categoryArray, halAlertInfo.urgency, halAlertInfo.severity,
+                halAlertInfo.certainty, halAlertInfo.description, alertAreaList,
+                halAlertInfo.language);
+    }
+
+    @VisibleForTesting
+    @Nullable static RadioAlert radioAlertFromHalAlert(Alert halAlert) {
+        if (halAlert == null) {
+            return null;
+        }
+        List<RadioAlert.AlertInfo> alertInfo = new ArrayList<>(halAlert.infoArray.length);
+        for (int idx = 0; idx < halAlert.infoArray.length; idx++) {
+            alertInfo.add(alertInfoFromHalAlertInfo(halAlert.infoArray[idx]));
+        }
+        // Integer values in android.hardware.radio.RadioAlert.AlertStatus and
+        // android.hardware.broadcastradio.AlertStatus match.
+        // Integer values in android.hardware.radio.RadioAlert.AlertMessageType and
+        // android.hardware.broadcastradio.AlertMessageType match.
+        return new RadioAlert(halAlert.status, halAlert.messageType, alertInfo);
+    }
+
     private static boolean isValidLogicallyTunedTo(ProgramIdentifier id) {
         return id.type == IdentifierType.AMFM_FREQUENCY_KHZ || id.type == IdentifierType.RDS_PI
                 || id.type == IdentifierType.HD_STATION_ID_EXT
@@ -605,7 +687,18 @@
                 }
             }
         }
-
+        if (!Flags.hdRadioEmergencyAlertSystem()) {
+            return new RadioManager.ProgramInfo(
+                    Objects.requireNonNull(programSelectorFromHalProgramSelector(info.selector)),
+                    identifierFromHalProgramIdentifier(info.logicallyTunedTo),
+                    identifierFromHalProgramIdentifier(info.physicallyTunedTo),
+                    relatedContent,
+                    info.infoFlags,
+                    info.signalQuality,
+                    radioMetadataFromHalMetadata(info.metadata),
+                    vendorInfoFromHalVendorKeyValues(info.vendorInfo)
+            );
+        }
         return new RadioManager.ProgramInfo(
                 Objects.requireNonNull(programSelectorFromHalProgramSelector(info.selector)),
                 identifierFromHalProgramIdentifier(info.logicallyTunedTo),
@@ -614,7 +707,8 @@
                 info.infoFlags,
                 info.signalQuality,
                 radioMetadataFromHalMetadata(info.metadata),
-                vendorInfoFromHalVendorKeyValues(info.vendorInfo)
+                vendorInfoFromHalVendorKeyValues(info.vendorInfo),
+                radioAlertFromHalAlert(info.emergencyAlert)
         );
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 0807c70..4ad7c10 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -26,6 +26,7 @@
 import android.view.DisplayCutout;
 import android.view.DisplayEventReceiver;
 import android.view.DisplayShape;
+import android.view.FrameRateCategoryRate;
 import android.view.RoundedCorners;
 import android.view.Surface;
 
@@ -300,6 +301,11 @@
     public boolean hasArrSupport;
 
     /**
+     * Represents frame rate for the FrameRateCategory Normal and High.
+     * @see android.view.Display#getSuggestedFrameRate(int) for more details.
+     */
+    public FrameRateCategoryRate frameRateCategoryRate;
+    /**
      * The default mode of the display.
      */
     public int defaultModeId;
@@ -548,7 +554,8 @@
                 || !Objects.equals(roundedCorners, other.roundedCorners)
                 || installOrientation != other.installOrientation
                 || !Objects.equals(displayShape, other.displayShape)
-                || hasArrSupport != other.hasArrSupport) {
+                || hasArrSupport != other.hasArrSupport
+                || !Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate)) {
             diff |= DIFF_OTHER;
         }
         return diff;
@@ -567,6 +574,7 @@
         modeId = other.modeId;
         renderFrameRate = other.renderFrameRate;
         hasArrSupport = other.hasArrSupport;
+        frameRateCategoryRate = other.frameRateCategoryRate;
         defaultModeId = other.defaultModeId;
         userPreferredModeId = other.userPreferredModeId;
         supportedModes = other.supportedModes;
@@ -612,6 +620,7 @@
         sb.append(", modeId ").append(modeId);
         sb.append(", renderFrameRate ").append(renderFrameRate);
         sb.append(", hasArrSupport ").append(hasArrSupport);
+        sb.append(", frameRateCategoryRate ").append(frameRateCategoryRate);
         sb.append(", defaultModeId ").append(defaultModeId);
         sb.append(", userPreferredModeId ").append(userPreferredModeId);
         sb.append(", supportedModes ").append(Arrays.toString(supportedModes));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3603cdb..f5a75c7d 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -25,7 +25,7 @@
 import static android.Manifest.permission.RESTRICT_DISPLAY_MODES;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
-import static android.hardware.display.DisplayManager.EventsMask;
+import static android.hardware.display.DisplayManager.EventFlag;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -1390,16 +1390,16 @@
     }
 
     private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid,
-            int callingUid, @EventsMask long eventsMask) {
+            int callingUid, @EventFlag long eventFlagsMask) {
         synchronized (mSyncRoot) {
             CallbackRecord record = mCallbacks.get(callingPid);
 
             if (record != null) {
-                record.updateEventsMask(eventsMask);
+                record.updateEventFlagsMask(eventFlagsMask);
                 return;
             }
 
-            record = new CallbackRecord(callingPid, callingUid, callback, eventsMask);
+            record = new CallbackRecord(callingPid, callingUid, callback, eventFlagsMask);
             try {
                 IBinder binder = callback.asBinder();
                 binder.linkToDeath(record, 0);
@@ -1889,6 +1889,7 @@
             final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
                     packageName, callingUid, virtualDisplayConfig);
 
+            boolean shouldClearDisplayWindowSettings = false;
             if (virtualDisplayConfig.isHomeSupported()) {
                 if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
                     Slog.w(TAG, "Display created with home support but lacks "
@@ -1900,6 +1901,18 @@
                 } else {
                     mWindowManagerInternal.setHomeSupportedOnDisplay(displayUniqueId,
                             Display.TYPE_VIRTUAL, true);
+                    shouldClearDisplayWindowSettings = true;
+                }
+            }
+
+            if (virtualDisplayConfig.isIgnoreActivitySizeRestrictions()) {
+                if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
+                    Slog.w(TAG, "Display created to ignore activity size restrictions, "
+                            + "but lacks VIRTUAL_DISPLAY_FLAG_TRUSTED, ignoring the request.");
+                } else {
+                    mWindowManagerInternal.setIgnoreActivitySizeRestrictionsOnDisplay(
+                            displayUniqueId, Display.TYPE_VIRTUAL, true);
+                    shouldClearDisplayWindowSettings = true;
                 }
             }
 
@@ -1922,8 +1935,7 @@
                 }
             }
 
-            if (displayId == Display.INVALID_DISPLAY && virtualDisplayConfig.isHomeSupported()
-                    && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
+            if (displayId == Display.INVALID_DISPLAY && shouldClearDisplayWindowSettings) {
                 // Failed to create the virtual display, so we should clean up the WM settings
                 // because it won't receive the onDisplayRemoved callback.
                 mWindowManagerInternal.clearDisplaySettings(displayUniqueId, Display.TYPE_VIRTUAL);
@@ -3997,7 +4009,7 @@
         public final int mPid;
         public final int mUid;
         private final IDisplayManagerCallback mCallback;
-        private @EventsMask AtomicLong mEventsMask;
+        private @DisplayManager.EventFlag AtomicLong mEventFlagsMask;
         private final String mPackageName;
 
         public boolean mWifiDisplayScanRequested;
@@ -4018,11 +4030,11 @@
         private boolean mFrozen;
 
         CallbackRecord(int pid, int uid, @NonNull IDisplayManagerCallback callback,
-                @EventsMask long eventsMask) {
+                @EventFlag long eventFlagsMask) {
             mPid = pid;
             mUid = uid;
             mCallback = callback;
-            mEventsMask = new AtomicLong(eventsMask);
+            mEventFlagsMask = new AtomicLong(eventFlagsMask);
             mCached = false;
             mFrozen = false;
 
@@ -4044,8 +4056,8 @@
             mPackageName = packageNames == null ? null : packageNames[0];
         }
 
-        public void updateEventsMask(@EventsMask long eventsMask) {
-            mEventsMask.set(eventsMask);
+        public void updateEventFlagsMask(@EventFlag long eventFlag) {
+            mEventFlagsMask.set(eventFlag);
         }
 
         /**
@@ -4109,12 +4121,13 @@
             if (!shouldSendEvent(event)) {
                 if (extraLogging(mPackageName)) {
                     Slog.i(TAG,
-                            "Not sending displayEvent: " + event + " due to mask:" + mEventsMask);
+                            "Not sending displayEvent: " + event + " due to flag:"
+                                    + mEventFlagsMask);
                 }
                 if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
                     Trace.instant(Trace.TRACE_TAG_POWER,
-                            "notifyDisplayEventAsync#notSendingEvent=" + event + ",mEventsMask="
-                                    + mEventsMask);
+                            "notifyDisplayEventAsync#notSendingEvent=" + event + ",mEventsFlag="
+                                    + mEventFlagsMask);
                 }
                 // The client is not interested in this event, so do nothing.
                 return true;
@@ -4160,22 +4173,22 @@
          * Return true if the client is interested in this event.
          */
         private boolean shouldSendEvent(@DisplayEvent int event) {
-            final long mask = mEventsMask.get();
+            final long flag = mEventFlagsMask.get();
             switch (event) {
                 case DisplayManagerGlobal.EVENT_DISPLAY_ADDED:
-                    return (mask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0;
+                    return (flag & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0;
                 case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
-                    return (mask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0;
+                    return (flag & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0;
                 case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED:
-                    return (mask & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0;
+                    return (flag & DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS) != 0;
                 case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED:
-                    return (mask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0;
+                    return (flag & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0;
                 case DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
-                    return (mask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0;
+                    return (flag & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0;
                 case DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED:
                     // fallthrough
                 case DisplayManagerGlobal.EVENT_DISPLAY_DISCONNECTED:
-                    return (mask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0;
+                    return (flag & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0;
                 default:
                     // This should never happen.
                     Slog.e(TAG, "Unknown display event " + event);
@@ -4369,7 +4382,7 @@
         @Override // Binder call
         @SuppressLint("AndroidFrameworkRequiresPermission") // Permission only required sometimes
         public void registerCallbackWithEventMask(IDisplayManagerCallback callback,
-                @EventsMask long eventsMask) {
+                @EventFlag long eventFlagsMask) {
             if (callback == null) {
                 throw new IllegalArgumentException("listener must not be null");
             }
@@ -4378,7 +4391,7 @@
             final int callingUid = Binder.getCallingUid();
 
             if (mFlags.isConnectedDisplayManagementEnabled()) {
-                if ((eventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+                if ((eventFlagsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
                     mContext.enforceCallingOrSelfPermission(MANAGE_DISPLAYS,
                             "Permission required to get signals about connection events.");
                 }
@@ -4386,7 +4399,7 @@
 
             final long token = Binder.clearCallingIdentity();
             try {
-                registerCallbackInternal(callback, callingPid, callingUid, eventsMask);
+                registerCallbackInternal(callback, callingPid, callingUid, eventFlagsMask);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index f9c3a46..a4bb8c3 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -45,6 +45,7 @@
 import android.view.DisplayCutout;
 import android.view.DisplayEventReceiver;
 import android.view.DisplayShape;
+import android.view.FrameRateCategoryRate;
 import android.view.RoundedCorners;
 import android.view.SurfaceControl;
 
@@ -247,6 +248,7 @@
         private boolean mDisplayModeSpecsInvalid;
         private int mActiveColorMode;
         private boolean mHasArrSupport;
+        private FrameRateCategoryRate mFrameRateCategoryRate;
         private Display.HdrCapabilities mHdrCapabilities;
         private boolean mAllmSupported;
         private boolean mGameContentTypeSupported;
@@ -313,6 +315,7 @@
             changed |= updateAllmSupport(dynamicInfo.autoLowLatencyModeSupported);
             changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported);
             changed |= updateHasArrSupportLocked(dynamicInfo.hasArrSupport);
+            changed |= updateFrameRateCategoryRatesLocked(dynamicInfo.frameRateCategoryRate);
 
             if (changed) {
                 mHavePendingChanges = true;
@@ -604,6 +607,15 @@
             return true;
         }
 
+        private boolean updateFrameRateCategoryRatesLocked(
+                FrameRateCategoryRate newFrameRateCategoryRate) {
+            if (Objects.equals(mFrameRateCategoryRate, newFrameRateCategoryRate)) {
+                return false;
+            }
+            mFrameRateCategoryRate = newFrameRateCategoryRate;
+            return true;
+        }
+
         private boolean updateHasArrSupportLocked(boolean newHasArrSupport) {
             if (mHasArrSupport == newHasArrSupport) {
                 return false;
@@ -695,6 +707,7 @@
                 }
                 mInfo.hdrCapabilities = mHdrCapabilities;
                 mInfo.hasArrSupport = mHasArrSupport;
+                mInfo.frameRateCategoryRate = mFrameRateCategoryRate;
                 mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos;
                 mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos;
                 mInfo.state = mState;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 074a4d8..7cfdcaf 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -507,6 +507,7 @@
             mBaseDisplayInfo.modeId = deviceInfo.modeId;
             mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate;
             mBaseDisplayInfo.hasArrSupport = deviceInfo.hasArrSupport;
+            mBaseDisplayInfo.frameRateCategoryRate = deviceInfo.frameRateCategoryRate;
             mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId;
             mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId;
             mBaseDisplayInfo.supportedModes = Arrays.copyOf(
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 36cadf5..a9bdcce 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -430,3 +430,19 @@
     bug: "350617205"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "enable_get_suggested_frame_rate"
+    namespace: "core_graphics"
+    description: "Flag for an API to get suggested frame rates"
+    bug: "361433796"
+    is_fixed_read_only: true
+}
+
+flag {
+    name: "enable_get_supported_refresh_rates"
+    namespace: "core_graphics"
+    description: "Flag to use the surfaceflinger rates for getSupportedRefreshRates"
+    bug: "365163968"
+    is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/state/DisplayStateController.java b/services/core/java/com/android/server/display/state/DisplayStateController.java
index dba6874..0b46e0f 100644
--- a/services/core/java/com/android/server/display/state/DisplayStateController.java
+++ b/services/core/java/com/android/server/display/state/DisplayStateController.java
@@ -61,7 +61,7 @@
         // We might override this below based on other factors.
         // Initialise brightness as invalid.
         int state;
-        int reason = Display.STATE_REASON_DEFAULT_POLICY;
+        int reason = displayPowerRequest.policyReason;
         switch (displayPowerRequest.policy) {
             case DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF:
                 state = Display.STATE_OFF;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 0766c3a..132d6fa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -3178,6 +3178,10 @@
         HdmiCecLocalDeviceSource source = playback();
         if (source == null) {
             source = audioSystem();
+        } else {
+            // Cancel an existing timer to send the device to sleep since OTP was triggered.
+            playback().mDelayedStandbyOnActiveSourceLostHandler
+                    .removeCallbacksAndMessages(null);
         }
 
         if (source == null) {
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..f4bd402
--- /dev/null
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -0,0 +1,141 @@
+/*
+ * 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.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.hardware.input.InputGestureData;
+import android.hardware.input.InputManager;
+import android.util.IndentingPrintWriter;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+
+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";
+
+    private static final int KEY_GESTURE_META_MASK =
+            KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON
+                    | KeyEvent.META_META_ON;
+
+    @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());
+        }
+    }
+
+    @Nullable
+    public InputGestureData getCustomGestureForKeyEvent(@UserIdInt int userId, KeyEvent event) {
+        final int keyCode = event.getKeyCode();
+        if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
+            return null;
+        }
+        synchronized (mCustomInputGestures) {
+            Map<InputGestureData.Trigger, InputGestureData> customGestures =
+                    mCustomInputGestures.get(userId);
+            if (customGestures == null) {
+                return null;
+            }
+            int modifierState = event.getMetaState() & KEY_GESTURE_META_MASK;
+            return customGestures.get(InputGestureData.createKeyTrigger(keyCode, modifierState));
+        }
+    }
+
+    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/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index a421d04..26929f5 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;
@@ -2985,8 +2987,42 @@
         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;
+        mKeyGestureController.setCurrentUserId(userId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index 4c5a3c2..9833016 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -89,6 +89,9 @@
     private static final int DEFAULT_FLAGS = 0;
     private static final boolean INJECT_ASYNC = true;
     private static final boolean INJECT_SYNC = false;
+    private static final long SECOND_IN_MILLISECONDS = 1000;
+
+    public static final int SWIPE_EVENT_HZ_DEFAULT = 120;
 
     /** Modifier key to meta state */
     private static final Map<Integer, Integer> MODIFIER;
@@ -519,11 +522,30 @@
         }
         long now = SystemClock.uptimeMillis();
         final long endTime = down + duration;
+        final float swipeEventPeriodMillis =
+                (float) SECOND_IN_MILLISECONDS / SWIPE_EVENT_HZ_DEFAULT;
+        int injected = 1;
         while (now < endTime) {
-            final long elapsedTime = now - down;
+            // Ensure that we inject at most at the frequency of SWIPE_EVENT_HZ_DEFAULT
+            // by waiting an additional delta between the actual time and expected time.
+            long elapsedTime = now - down;
+            final long errorMillis =
+                    (long) Math.floor(injected * swipeEventPeriodMillis - elapsedTime);
+            if (errorMillis > 0) {
+                // Make sure not to exceed the duration and inject an extra event.
+                if (errorMillis > endTime - now) {
+                    sleep(endTime - now);
+                    break;
+                }
+                sleep(errorMillis);
+            }
+
+            now = SystemClock.uptimeMillis();
+            elapsedTime = now - down;
             final float alpha = (float) elapsedTime / duration;
             injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
                     lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
+            injected++;
             now = SystemClock.uptimeMillis();
         }
         injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index 4d93e65..ebeef65 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -25,20 +25,27 @@
 import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures;
 import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
 import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut;
+import static com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts;
 
 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.AppLaunchData;
 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 +73,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;
@@ -84,6 +92,9 @@
 
     // Maximum key gesture events that are tracked and will be available in input dump.
     private static final int MAX_TRACKED_EVENTS = 10;
+    private static final int SHORTCUT_META_MASK =
+            KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON
+                    | KeyEvent.META_SHIFT_ON;
 
     private static final int MSG_NOTIFY_KEY_GESTURE_EVENT = 1;
 
@@ -108,6 +119,11 @@
     private final int mSystemPid;
     private final KeyCombinationManager mKeyCombinationManager;
     private final SettingsObserver mSettingsObserver;
+    private final InputGestureManager mInputGestureManager = new InputGestureManager();
+    private static final Object mUserLock = new Object();
+    @UserIdInt
+    @GuardedBy("mUserLock")
+    private int mCurrentUserId = UserHandle.USER_SYSTEM;
 
     // Pending actions
     private boolean mPendingMetaAction;
@@ -221,7 +237,7 @@
 
                         @Override
                         public void execute() {
-                            handleKeyGesture(
+                            handleMultiKeyGesture(
                                     new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_POWER},
                                     KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
                                     KeyGestureEvent.ACTION_GESTURE_START, 0);
@@ -229,7 +245,7 @@
 
                         @Override
                         public void cancel() {
-                            handleKeyGesture(
+                            handleMultiKeyGesture(
                                     new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_POWER},
                                     KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
                                     KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -249,14 +265,14 @@
 
                             @Override
                             public void execute() {
-                                handleKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
+                                handleMultiKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
                                                 KeyEvent.KEYCODE_STEM_PRIMARY},
                                         KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
                                         KeyGestureEvent.ACTION_GESTURE_START, 0);
                             }
                             @Override
                             public void cancel() {
-                                handleKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
+                                handleMultiKeyGesture(new int[]{KeyEvent.KEYCODE_POWER,
                                                 KeyEvent.KEYCODE_STEM_PRIMARY},
                                         KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
                                         KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -277,7 +293,7 @@
 
                     @Override
                     public void execute() {
-                        handleKeyGesture(
+                        handleMultiKeyGesture(
                                 new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP},
                                 KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
                                 KeyGestureEvent.ACTION_GESTURE_START, 0);
@@ -285,7 +301,7 @@
 
                     @Override
                     public void cancel() {
-                        handleKeyGesture(
+                        handleMultiKeyGesture(
                                 new int[]{KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP},
                                 KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -317,7 +333,7 @@
                         if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
                             return;
                         }
-                        handleKeyGesture(
+                        handleMultiKeyGesture(
                                 new int[]{KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_POWER},
                                 gestureType, KeyGestureEvent.ACTION_GESTURE_START, 0);
                     }
@@ -327,7 +343,7 @@
                         if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
                             return;
                         }
-                        handleKeyGesture(
+                        handleMultiKeyGesture(
                                 new int[]{KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_POWER},
                                 gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE,
                                 KeyGestureEvent.FLAG_CANCELLED);
@@ -361,7 +377,7 @@
 
                         @Override
                         public void execute() {
-                            handleKeyGesture(
+                            handleMultiKeyGesture(
                                     new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
                                     KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
                                     KeyGestureEvent.ACTION_GESTURE_START, 0);
@@ -369,7 +385,7 @@
 
                         @Override
                         public void cancel() {
-                            handleKeyGesture(
+                            handleMultiKeyGesture(
                                     new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
                                     KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
                                     KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -398,14 +414,14 @@
 
                         @Override
                         public void execute() {
-                            handleKeyGesture(
+                            handleMultiKeyGesture(
                                     new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER},
                                     KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
                                     KeyGestureEvent.ACTION_GESTURE_START, 0);
                         }
                         @Override
                         public void cancel() {
-                            handleKeyGesture(
+                            handleMultiKeyGesture(
                                     new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER},
                                     KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
                                     KeyGestureEvent.ACTION_GESTURE_COMPLETE,
@@ -480,7 +496,7 @@
     private boolean interceptSystemKeysAndShortcuts(IBinder focusedToken, KeyEvent event) {
         final int keyCode = event.getKeyCode();
         final int repeatCount = event.getRepeatCount();
-        final int metaState = event.getMetaState();
+        final int metaState = event.getMetaState() & SHORTCUT_META_MASK;
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final boolean canceled = event.isCanceled();
         final int displayId = event.getDisplayId();
@@ -503,7 +519,7 @@
                     return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_RECENT_APPS:
@@ -511,7 +527,7 @@
                     handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_APP_SWITCH:
@@ -519,12 +535,13 @@
                     handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
                             KeyGestureEvent.ACTION_GESTURE_START, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 } else if (!down) {
                     handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, canceled ? KeyGestureEvent.FLAG_CANCELLED : 0);
+                            focusedToken, canceled ? KeyGestureEvent.FLAG_CANCELLED : 0,
+                            /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_H:
@@ -533,7 +550,7 @@
                     return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_I:
@@ -541,7 +558,7 @@
                     return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_L:
@@ -549,7 +566,7 @@
                     return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_N:
@@ -559,13 +576,13 @@
                                 KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     } else {
                         return handleKeyGesture(deviceId, new int[]{keyCode},
                                 KeyEvent.META_META_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     }
                 }
                 break;
@@ -575,7 +592,7 @@
                             KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_T:
@@ -585,7 +602,55 @@
                                 KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
+                    }
+                }
+                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, /* appLaunchData = */null);
+                    }
+                }
+                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, /* appLaunchData = */null);
+                    }
+                }
+                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, /* appLaunchData = */null);
+                    }
+                }
+                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, /* appLaunchData = */null);
                     }
                 }
                 break;
@@ -597,7 +662,7 @@
                                 KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     }
                 }
                 // fall through
@@ -606,7 +671,7 @@
                     return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_DPAD_UP:
@@ -615,7 +680,7 @@
                             KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_DPAD_DOWN:
@@ -624,7 +689,7 @@
                             KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_DPAD_LEFT:
@@ -634,19 +699,19 @@
                                 KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     } else if (event.isAltPressed()) {
                         return handleKeyGesture(deviceId, new int[]{keyCode},
                                 KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     } else {
                         return handleKeyGesture(deviceId, new int[]{keyCode},
                                 KeyEvent.META_META_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     }
                 }
                 break;
@@ -657,13 +722,13 @@
                                 KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     } else if (event.isAltPressed()) {
                         return handleKeyGesture(deviceId, new int[]{keyCode},
                                 KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     }
                 }
                 break;
@@ -674,8 +739,51 @@
                                 KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE,
-                                displayId,
-                                focusedToken, /* flags = */0);
+                                displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_LEFT_BRACKET:
+                if (enableTaskResizingKeyboardShortcuts()) {
+                    if (firstDown && event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_RIGHT_BRACKET:
+                if (enableTaskResizingKeyboardShortcuts()) {
+                    if (firstDown && event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_EQUALS:
+                if (enableTaskResizingKeyboardShortcuts()) {
+                    if (firstDown && event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_MINUS:
+                if (enableTaskResizingKeyboardShortcuts()) {
+                    if (firstDown && event.isAltPressed()) {
+                        return handleKeyGesture(deviceId, new int[]{keyCode},
+                                KeyEvent.META_ALT_ON,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
+                                KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                                displayId, focusedToken, /* flags = */0, /* appLaunchData = */null);
                     }
                 }
                 break;
@@ -684,7 +792,7 @@
                     return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_BRIGHTNESS_UP:
@@ -695,7 +803,7 @@
                                     ? KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP
                                     : KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
@@ -703,7 +811,7 @@
                     handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
@@ -711,7 +819,7 @@
                     handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
@@ -720,7 +828,7 @@
                     handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_ALL_APPS:
@@ -728,7 +836,7 @@
                     handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_NOTIFICATION:
@@ -736,7 +844,7 @@
                     handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_SEARCH:
@@ -744,7 +852,7 @@
                     return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
 
                 }
                 break;
@@ -755,13 +863,13 @@
                                 new int[]{keyCode}, /* modifierState = */0,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     } else if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL) {
                         handleKeyGesture(deviceId,
                                 new int[]{keyCode}, /* modifierState = */0,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     }
                 }
                 return true;
@@ -771,7 +879,7 @@
                             event.isShiftPressed() ? KeyEvent.META_SHIFT_ON : 0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_CAPS_LOCK:
@@ -781,7 +889,8 @@
                     AidlKeyGestureEvent eventToNotify = createKeyGestureEvent(deviceId,
                             new int[]{keyCode}, metaState,
                             KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
-                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, /* flags = */0);
+                            KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, /* flags = */0,
+                            /* appLaunchData = */null);
                     Message msg = Message.obtain(mHandler, MSG_NOTIFY_KEY_GESTURE_EVENT,
                             eventToNotify);
                     mHandler.sendMessage(msg);
@@ -792,7 +901,7 @@
                     handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 return true;
             case KeyEvent.KEYCODE_META_LEFT:
@@ -813,7 +922,7 @@
                                         KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
 
                     } else if (mPendingMetaAction) {
                         mPendingMetaAction = false;
@@ -822,7 +931,7 @@
                                     /* modifierState = */0,
                                     KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
                                     KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                    focusedToken, /* flags = */0);
+                                    focusedToken, /* flags = */0, /* appLaunchData = */null);
                         }
                     }
                 }
@@ -833,7 +942,7 @@
                         return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     } else if (!mPendingHideRecentSwitcher) {
                         final int shiftlessModifiers =
                                 event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
@@ -844,7 +953,7 @@
                                     KeyEvent.META_ALT_ON,
                                     KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
                                     KeyGestureEvent.ACTION_GESTURE_START, displayId,
-                                    focusedToken, /* flags = */0);
+                                    focusedToken, /* flags = */0, /* appLaunchData = */null);
                         }
                     }
                 }
@@ -865,7 +974,7 @@
                                 KeyEvent.META_ALT_ON,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     }
 
                     // Toggle Caps Lock on META-ALT.
@@ -875,7 +984,7 @@
                                         KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0,
                                 KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     }
                 }
                 break;
@@ -894,6 +1003,22 @@
                         + " interceptKeyBeforeQueueing");
                 return true;
         }
+
+        if (firstDown) {
+            InputGestureData customGesture;
+            synchronized (mUserLock) {
+                customGesture = mInputGestureManager.getCustomGestureForKeyEvent(mCurrentUserId,
+                        event);
+            }
+            if (customGesture == null) {
+                return false;
+            }
+            return handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
+                    customGesture.getAction().keyGestureType(),
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE,
+                    displayId, focusedToken, /* flags = */0,
+                    customGesture.getAction().appLaunchData());
+        }
         return false;
     }
 
@@ -916,7 +1041,7 @@
                                         ? KeyEvent.META_SHIFT_ON : 0),
                                 KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
                                 KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                                focusedToken, /* flags = */0);
+                                focusedToken, /* flags = */0, /* appLaunchData = */null);
                     }
                 }
                 break;
@@ -928,7 +1053,7 @@
                             KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
                             KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_SYSRQ:
@@ -936,7 +1061,7 @@
                     return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
             case KeyEvent.KEYCODE_ESCAPE:
@@ -944,7 +1069,7 @@
                     return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
                             KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
                             KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
-                            focusedToken, /* flags = */0);
+                            focusedToken, /* flags = */0, /* appLaunchData = */null);
                 }
                 break;
         }
@@ -952,18 +1077,19 @@
         return false;
     }
 
-    private boolean handleKeyGesture(int[] keycodes,
+    private void handleMultiKeyGesture(int[] keycodes,
             @KeyGestureEvent.KeyGestureType int gestureType, int action, int flags) {
-        return handleKeyGesture(KeyCharacterMap.VIRTUAL_KEYBOARD, keycodes, /* modifierState= */0,
-                gestureType, action, Display.DEFAULT_DISPLAY, /* focusedToken = */null, flags);
+        handleKeyGesture(KeyCharacterMap.VIRTUAL_KEYBOARD, keycodes, /* modifierState= */0,
+                gestureType, action, Display.DEFAULT_DISPLAY, /* focusedToken = */null, flags,
+                /* appLaunchData = */null);
     }
 
     @VisibleForTesting
     boolean handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
             @KeyGestureEvent.KeyGestureType int gestureType, int action, int displayId,
-            @Nullable IBinder focusedToken, int flags) {
+            @Nullable IBinder focusedToken, int flags, @Nullable AppLaunchData appLaunchData) {
         return handleKeyGesture(createKeyGestureEvent(deviceId, keycodes,
-                modifierState, gestureType, action, displayId, flags), focusedToken);
+                modifierState, gestureType, action, displayId, flags, appLaunchData), focusedToken);
     }
 
     private boolean handleKeyGesture(AidlKeyGestureEvent event, @Nullable IBinder focusedToken) {
@@ -995,18 +1121,27 @@
         // TODO(b/358569822): Once we move the gesture detection logic to IMS, we ideally
         //  should not rely on PWM to tell us about the gesture start and end.
         AidlKeyGestureEvent event = createKeyGestureEvent(deviceId, keycodes, modifierState,
-                gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY, 0);
+                gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY,
+                /* flags = */0, /* appLaunchData = */null);
         mHandler.obtainMessage(MSG_NOTIFY_KEY_GESTURE_EVENT, event).sendToTarget();
     }
 
     public void handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
             @KeyGestureEvent.KeyGestureType int gestureType) {
         AidlKeyGestureEvent event = createKeyGestureEvent(deviceId, keycodes, modifierState,
-                gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY, 0);
+                gestureType, KeyGestureEvent.ACTION_GESTURE_COMPLETE, Display.DEFAULT_DISPLAY,
+                /* flags = */0, /* appLaunchData = */null);
         handleKeyGesture(event, null /*focusedToken*/);
     }
 
     @MainThread
+    public void setCurrentUserId(@UserIdInt int userId) {
+        synchronized (mUserLock) {
+            mCurrentUserId = userId;
+        }
+    }
+
+    @MainThread
     private void notifyKeyGestureEvent(AidlKeyGestureEvent event) {
         InputDevice device = getInputDevice(event.deviceId);
         if (device == null) {
@@ -1085,6 +1220,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);
@@ -1232,7 +1398,7 @@
 
     private AidlKeyGestureEvent createKeyGestureEvent(int deviceId, int[] keycodes,
             int modifierState, @KeyGestureEvent.KeyGestureType int gestureType, int action,
-            int displayId, int flags) {
+            int displayId, int flags, @Nullable AppLaunchData appLaunchData) {
         AidlKeyGestureEvent event = new AidlKeyGestureEvent();
         event.deviceId = deviceId;
         event.keycodes = keycodes;
@@ -1241,6 +1407,18 @@
         event.action = action;
         event.displayId = displayId;
         event.flags = flags;
+        if (appLaunchData != null) {
+            if (appLaunchData instanceof AppLaunchData.CategoryData categoryData) {
+                event.appLaunchCategory = categoryData.getCategory();
+            } else if (appLaunchData instanceof AppLaunchData.RoleData roleData) {
+                event.appLaunchRole = roleData.getRole();
+            } else if (appLaunchData instanceof AppLaunchData.ComponentData componentData) {
+                event.appLaunchPackageName = componentData.getPackageName();
+                event.appLaunchClassName = componentData.getClassName();
+            } else {
+                throw new IllegalArgumentException("AppLaunchData type is invalid!");
+            }
+        }
         return event;
     }
 
@@ -1286,5 +1464,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/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 131b9ba..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);
@@ -6078,8 +6091,6 @@
         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;
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 408df5a..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
+++ /dev/null
@@ -1,54 +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.
- */
-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/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/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index bbdac56..c314ab0 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -253,10 +253,10 @@
 
     private static final String MIGRATED_FRP2 = "migrated_frp2";
     private static final String MIGRATED_KEYSTORE_NS = "migrated_keystore_namespace";
-    private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
     private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";
     private static final String MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS =
             "migrated_weaver_disabled_on_unsecured_users";
+    // Note: some other migrated_* strings used to be used and may exist in the database already.
 
     // Duration that LockSettingsService will store the gatekeeper password for. This allows
     // multiple biometric enrollments without prompting the user to enter their password via
@@ -347,6 +347,8 @@
 
     private final StorageManagerInternal mStorageManagerInternal;
 
+    private final Object mGcWorkToken = new Object();
+
     // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
     // devices. The most basic of these is to show/hide notifications about missing features until
     // the user unlocks the account and credential-encrypted storage is available.
@@ -1183,9 +1185,7 @@
 
             // If config_disableWeaverOnUnsecuredUsers=true, then the Weaver HAL may be buggy and
             // need multiple retries before it works here to unwrap the SP, if the SP was already
-            // protected by Weaver.  Note that the problematic HAL can also deadlock if called with
-            // the ActivityManagerService lock held, but that should not be a problem here since
-            // that lock isn't held here, unlike unlockUserKeyIfUnsecured() where it is.
+            // protected by Weaver.
             for (int i = 0; i < 12 && sp == null; i++) {
                 Slog.e(TAG, "Failed to unwrap synthetic password. Waiting 5 seconds to retry.");
                 SystemClock.sleep(5000);
@@ -1221,21 +1221,16 @@
                 Slog.i(TAG, "Synthetic password is already not protected by Weaver");
             }
         } else if (sp == null) {
-            Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
-            return;
+            throw new IllegalStateException(
+                    "Failed to unwrap synthetic password for unsecured user " + userId);
         }
 
         // Call setCeStorageProtection(), to re-encrypt the CE key with the SP if it's currently
-        // encrypted by an empty secret.  Skip this if it was definitely already done as part of the
-        // upgrade to Android 14, since while setCeStorageProtection() is idempotent it does log
-        // some error messages when called again.  Do not skip this if
-        // config_disableWeaverOnUnsecuredUsers=true, since in that case we'd like to recover from
-        // the case where an earlier upgrade to Android 14 incorrectly skipped this step.
-        if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null
-                || isWeaverDisabledOnUnsecuredUsers()) {
-            Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
-            setCeStorageProtection(userId, sp);
-        }
+        // encrypted by an empty secret.  If the CE key is already encrypted by the SP, then this is
+        // a no-op except for some log messages.
+        Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
+        setCeStorageProtection(userId, sp);
+
         Slogf.i(TAG, "Initializing Keystore super keys for user %d", userId);
         initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true);
     }
@@ -3639,11 +3634,19 @@
      * release references to the argument.
      */
     private void scheduleGc() {
+        // Cancel any existing GC request first, so that GC requests don't pile up if lockscreen
+        // credential operations are happening very quickly, e.g. as sometimes happens during tests.
+        //
+        // This delays the already-requested GC, but that is fine in practice where lockscreen
+        // operations don't happen very quickly.  And the precise time that the sanitization happens
+        // isn't very important; doing it within a minute can be fine, for example.
+        mHandler.removeCallbacksAndMessages(mGcWorkToken);
+
         mHandler.postDelayed(() -> {
             System.gc();
             System.runFinalization();
             System.gc();
-        }, 2000);
+        }, mGcWorkToken, 2000);
     }
 
     private class DeviceProvisionedObserver extends ContentObserver {
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index 25e1c94..6bc4098 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -32,6 +32,7 @@
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.MediaRoute2Info;
+import android.media.audio.Flags;
 import android.media.audiopolicy.AudioProductStrategy;
 import android.os.Handler;
 import android.os.HandlerExecutor;
@@ -711,5 +712,13 @@
                         MediaRoute2Info.TYPE_DOCK,
                         /* defaultRouteId= */ "ROUTE_ID_DOCK_ANALOG",
                         /* nameResource= */ R.string.default_audio_route_name_dock_speakers));
+        if (Flags.enableMultichannelGroupDevice()) {
+            AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.put(
+                    AudioDeviceInfo.TYPE_MULTICHANNEL_GROUP,
+                    new SystemRouteInfo(
+                            MediaRoute2Info.TYPE_MULTICHANNEL_SPEAKER_GROUP,
+                            /* defaultRouteId= */ "ROUTE_ID_MULTICHANNEL_SPEAKER_GROUP",
+                            /* nameResource= */ R.string.default_audio_route_name_external_device));
+        }
     }
 }
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..d265b6a
--- /dev/null
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media.quality;
+
+import android.content.Context;
+import android.media.quality.IMediaQualityManager;
+import android.media.quality.PictureProfile;
+
+import com.android.server.SystemService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 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 {
+        @Override
+        public PictureProfile createPictureProfile(PictureProfile pp) {
+            // TODO: implement
+            return pp;
+        }
+        @Override
+        public PictureProfile getPictureProfileById(long id) {
+            return null;
+        }
+        @Override
+        public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
+            return new ArrayList<>();
+        }
+        @Override
+        public List<PictureProfile> getAvailablePictureProfiles() {
+            return new ArrayList<>();
+        }
+        @Override
+        public List<PictureProfile> getAvailableAllPictureProfiles() {
+            return new ArrayList<>();
+        }
+    }
+}
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/NotificationBackupHelper.java b/services/core/java/com/android/server/notification/NotificationBackupHelper.java
index ee9ec15..9df44a4 100644
--- a/services/core/java/com/android/server/notification/NotificationBackupHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationBackupHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import static android.app.backup.NotificationLoggingConstants.KEY_NOTIFICATIONS;
+
 import android.app.INotificationManager;
 import android.app.backup.BlobBackupHelper;
 import android.os.ServiceManager;
@@ -31,9 +33,6 @@
     // Current version of the blob schema
     static final int BLOB_VERSION = 1;
 
-    // Key under which the payload blob is stored
-    static final String KEY_NOTIFICATIONS = "notifications";
-
     private final int mUserId;
 
     private final NotificationManagerInternal mNm;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index abf3da4..2c45fc8 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -105,6 +105,7 @@
 import static android.service.notification.Adjustment.KEY_TYPE;
 import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
 import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
 import static android.service.notification.Flags.callstyleCallbackApi;
 import static android.service.notification.Flags.notificationClassification;
 import static android.service.notification.Flags.notificationForceGrouping;
@@ -161,6 +162,8 @@
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING;
 import static com.android.server.notification.Flags.expireBitmaps;
 import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER;
 import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
@@ -1098,7 +1101,7 @@
     }
 
     void readPolicyXml(InputStream stream, boolean forRestore, int userId,
-            BackupRestoreEventLogger logger)
+            @Nullable BackupRestoreEventLogger logger)
             throws XmlPullParserException, NumberFormatException, IOException {
         final TypedXmlPullParser parser;
         if (forRestore) {
@@ -1114,7 +1117,27 @@
         int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
             if (ZenModeConfig.ZEN_TAG.equals(parser.getName())) {
-                mZenModeHelper.readXml(parser, forRestore, userId);
+                int successfulReads = 0;
+                int unsuccessfulReads = 0;
+                try {
+                    boolean loadedCorrectly =
+                            mZenModeHelper.readXml(parser, forRestore, userId, logger);
+                    if (loadedCorrectly)
+                        successfulReads++;
+                    else
+                        unsuccessfulReads++;
+                } catch (Exception e) {
+                    Slog.wtf(TAG, "failed to read config", e);
+                    unsuccessfulReads++;
+                }
+                if (logger != null) {
+                    logger.logItemsRestored(DATA_TYPE_ZEN_CONFIG, successfulReads);
+                    if (unsuccessfulReads > 0) {
+                        logger.logItemsRestoreFailed(
+                                DATA_TYPE_ZEN_CONFIG, unsuccessfulReads, ERROR_XML_PARSING);
+                    }
+                }
+
             } else if (PreferencesHelper.TAG_RANKING.equals(parser.getName())){
                 mPreferencesHelper.readXml(parser, forRestore, userId);
             }
@@ -1246,7 +1269,7 @@
         }
     }
 
-    private void writePolicyXml(OutputStream stream, boolean forBackup, int userId,
+    void writePolicyXml(OutputStream stream, boolean forBackup, int userId,
             BackupRestoreEventLogger logger)  throws IOException {
         final TypedXmlSerializer out;
         if (forBackup) {
@@ -1258,7 +1281,7 @@
         out.startDocument(null, true);
         out.startTag(null, TAG_NOTIFICATION_POLICY);
         out.attributeInt(null, ATTR_VERSION, DB_VERSION);
-        mZenModeHelper.writeXml(out, forBackup, null, userId);
+        mZenModeHelper.writeXml(out, forBackup, null, userId, logger);
         mPreferencesHelper.writeXml(out, forBackup, userId);
         mListeners.writeXml(out, forBackup, userId);
         mAssistants.writeXml(out, forBackup, userId);
@@ -5683,14 +5706,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);
                     });
@@ -5745,12 +5770,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);
             }
@@ -5760,7 +5786,7 @@
         @Override
         public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
             enforcePolicyAccess(Binder.getCallingUid(), "getZenRules");
-            return mZenModeHelper.getZenRules();
+            return mZenModeHelper.getZenRules(getCallingZenUser());
         }
 
         @Override
@@ -5769,14 +5795,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
@@ -5791,6 +5817,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.
@@ -5801,16 +5828,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
@@ -5819,8 +5848,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());
         }
 
@@ -5886,8 +5916,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());
         }
 
@@ -5897,9 +5928,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
@@ -5907,7 +5940,7 @@
             Objects.requireNonNull(owner, "Owner is null");
             enforceSystemOrSystemUI("getRuleInstanceCount");
 
-            return mZenModeHelper.getCurrentInstanceCount(owner);
+            return mZenModeHelper.getCurrentInstanceCount(getCallingZenUser(), owner);
         }
 
         @Override
@@ -5915,7 +5948,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
@@ -5926,9 +5959,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
@@ -5964,15 +6018,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 {
@@ -6268,12 +6323,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);
             }
@@ -6302,6 +6358,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()
@@ -6311,7 +6368,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;
@@ -6369,11 +6426,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);
@@ -6624,6 +6682,33 @@
         }
 
         @Override
+        @FlaggedApi(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+        public NotificationChannel createConversationNotificationChannelForPackageFromPrivilegedListener(
+                INotificationListener token, String pkg, UserHandle user,
+                String parentId, String conversationId) throws RemoteException {
+            Objects.requireNonNull(pkg);
+            Objects.requireNonNull(user);
+            Objects.requireNonNull(parentId);
+            Objects.requireNonNull(conversationId);
+
+            verifyPrivilegedListener(token, user, true);
+
+            int uid = getUidForPackageAndUser(pkg, user);
+            NotificationChannel conversationChannel =
+                    mPreferencesHelper.getNotificationChannel(pkg, uid, parentId, false).copy();
+            String conversationChannelId = String.format(
+                    CONVERSATION_CHANNEL_ID_FORMAT, parentId, conversationId);
+            conversationChannel.setId(conversationChannelId);
+            conversationChannel.setConversationId(parentId, conversationId);
+            createNotificationChannelsImpl(
+                    pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel)));
+            handleSavePolicyFile();
+
+            return mPreferencesHelper.getConversationNotificationChannel(
+                    pkg, uid, parentId, conversationId, false, false).copy();
+        }
+
+        @Override
         public void updateNotificationChannelGroupFromPrivilegedListener(
                 INotificationListener token, String pkg, UserHandle user,
                 NotificationChannelGroup group) throws RemoteException {
@@ -6641,7 +6726,7 @@
             Objects.requireNonNull(pkg);
             Objects.requireNonNull(user);
 
-            verifyPrivilegedListener(token, user, false);
+            verifyPrivilegedListener(token, user, true);
 
             final NotificationChannel originalChannel = mPreferencesHelper.getNotificationChannel(
                     pkg, getUidForPackageAndUser(pkg, user), channel.getId(), true);
@@ -9278,10 +9363,15 @@
                                             // a group summary or children (complete a group)
                                             mHandler.postDelayed(() -> {
                                                 synchronized (mNotificationLock) {
-                                                    mGroupHelper.onNotificationPostedWithDelay(
-                                                        r, mNotificationList, mSummaryByGroupKey);
+                                                    NotificationRecord record =
+                                                            mNotificationsByKey.get(key);
+                                                    if (record != null) {
+                                                        mGroupHelper.onNotificationPostedWithDelay(
+                                                                record, mNotificationList,
+                                                                mSummaryByGroupKey);
+                                                    }
                                                 }
-                                            }, r.getKey(), DELAY_FORCE_REGROUP_TIME);
+                                            }, key, DELAY_FORCE_REGROUP_TIME);
                                         }
                                     }
 
@@ -9327,10 +9417,15 @@
                                     if (notificationForceGrouping()) {
                                         mHandler.postDelayed(() -> {
                                             synchronized (mNotificationLock) {
-                                                mGroupHelper.onNotificationPostedWithDelay(r,
-                                                        mNotificationList, mSummaryByGroupKey);
+                                                NotificationRecord record =
+                                                        mNotificationsByKey.get(key);
+                                                if (record != null) {
+                                                    mGroupHelper.onNotificationPostedWithDelay(
+                                                            record, mNotificationList,
+                                                            mSummaryByGroupKey);
+                                                }
                                             }
-                                        }, r.getKey(), DELAY_FORCE_REGROUP_TIME);
+                                        }, key, DELAY_FORCE_REGROUP_TIME);
                                     }
                                 }
                             }
@@ -10310,7 +10405,7 @@
                 }
                 mListeners.notifyRemovedLocked(r, reason, r.getStats());
                 if (notificationForceGrouping()) {
-                    mHandler.removeCallbacksAndMessages(r.getKey());
+                    mHandler.removeCallbacksAndEqualMessages(r.getKey());
                     mHandler.post(() -> {
                         synchronized (NotificationManagerService.this.mNotificationLock) {
                             mGroupHelper.onNotificationRemoved(r, mNotificationList);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index d26a5aa..9f0b4b0 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -2028,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);
@@ -2072,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);
@@ -2099,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,
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..d5f13a8 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -40,9 +40,12 @@
 import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_ACTIVATE;
 import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
 import static android.service.notification.ZenModeConfig.implicitRuleId;
+import static android.service.notification.ZenModeConfig.isImplicitRuleId;
 
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.internal.util.Preconditions.checkArgument;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING;
 
 import static java.util.Objects.requireNonNull;
 
@@ -56,6 +59,7 @@
 import android.app.Flags;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
+import android.app.backup.BackupRestoreEventLogger;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
@@ -167,15 +171,13 @@
     private final Clock mClock;
     private final SettingsObserver mSettingsObserver;
     private final AppOpsManager mAppOps;
-    private final NotificationManager mNotificationManager;
     private final ZenModeConfig mDefaultConfig;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final ZenModeFiltering mFiltering;
     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;
@@ -215,15 +217,14 @@
         mClock = clock;
         addCallback(mMetrics);
         mAppOps = context.getSystemService(AppOpsManager.class);
-        mNotificationManager = context.getSystemService(NotificationManager.class);
 
         mDefaultConfig = Flags.modesUi()
                 ? ZenModeConfig.getDefaultConfig()
                 : 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 +238,6 @@
         mZenModeEventLogger = zenModeEventLogger;
     }
 
-    public Looper getLooper() {
-        return mHandler.getLooper();
-    }
-
     @Override
     public String toString() {
         return TAG;
@@ -331,7 +328,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 +347,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 +373,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 +398,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 +417,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 +426,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 +440,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 +457,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 +469,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 +527,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 +561,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 +624,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,9 +657,14 @@
                     // 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);
+                } else {
+                    if (Flags.modesUi()) {
+                        updateImplicitZenRuleNameAndDescription(rule);
+                    }
                 }
+
                 // If the user has changed the rule's *zenMode*, then don't let app overwrite it.
                 // We allow the update if the user has only changed other aspects of the rule.
                 if ((rule.userModifiedFields & AutomaticZenRule.FIELD_INTERRUPTION_FILTER) == 0) {
@@ -680,17 +692,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) {
@@ -698,7 +711,12 @@
                 rule = newImplicitZenRule(callingPkg);
                 rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
                 newConfig.automaticRules.put(rule.id, rule);
+            } else {
+                if (Flags.modesUi()) {
+                    updateImplicitZenRuleNameAndDescription(rule);
+                }
             }
+
             // If the user has changed the rule's *ZenPolicy*, then don't let app overwrite it.
             // We allow the update if the user has only changed other aspects of the rule.
             if (rule.zenPolicyUserModifiedFields == 0) {
@@ -709,9 +727,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 +753,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);
             }
         }
     }
@@ -766,24 +786,8 @@
         rule.id = implicitRuleId(pkg);
         rule.pkg = pkg;
         rule.creationTime = mClock.millis();
-
-        Binder.withCleanCallingIdentity(() -> {
-            try {
-                ApplicationInfo applicationInfo = mPm.getApplicationInfo(pkg, 0);
-                rule.name = applicationInfo.loadLabel(mPm).toString();
-                if (!Flags.modesUi()) {
-                    rule.iconResName = drawableResIdToResName(pkg, applicationInfo.icon);
-                }
-            } catch (PackageManager.NameNotFoundException e) {
-                // Should not happen, since it's the app calling us (?)
-                Log.w(TAG, "Package not found for creating implicit zen rule");
-                rule.name = "Unknown";
-            }
-        });
-
+        updateImplicitZenRuleNameAndDescription(rule);
         rule.type = AutomaticZenRule.TYPE_OTHER;
-        rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description,
-                rule.name);
         rule.condition = null;
         rule.conditionId = new Uri.Builder()
                 .scheme(Condition.SCHEME)
@@ -798,13 +802,46 @@
         return rule;
     }
 
-    boolean removeAutomaticZenRule(String id, @ConfigOrigin int origin, String reason,
-            int callingUid) {
+    private void updateImplicitZenRuleNameAndDescription(ZenRule rule) {
+        checkArgument(isImplicitRuleId(rule.id));
+        requireNonNull(rule.pkg, "Implicit rule is not associated to package yet!");
+
+        String pkgAppName = Binder.withCleanCallingIdentity(() -> {
+            try {
+                ApplicationInfo applicationInfo = mPm.getApplicationInfo(rule.pkg, 0);
+                return applicationInfo.loadLabel(mPm).toString();
+            } catch (PackageManager.NameNotFoundException e) {
+                // Should not happen. When creating it's the app calling us, and when updating
+                // the rule would've been deleted if the package was removed.
+                Slog.e(TAG, "Package not found when updating implicit zen rule name", e);
+                return null;
+            }
+        });
+
+        if (pkgAppName != null) {
+            if ((rule.userModifiedFields & AutomaticZenRule.FIELD_NAME) == 0) {
+                if (Flags.modesUi()) {
+                    rule.name = mContext.getString(R.string.zen_mode_implicit_name, pkgAppName);
+                } else {
+                    rule.name = pkgAppName;
+                }
+            }
+            rule.triggerDescription = mContext.getString(
+                    R.string.zen_mode_implicit_trigger_description, pkgAppName);
+        } else if (rule.name == null) {
+            // We must give a new rule SOME name. But this path should never be hit.
+            rule.name = "Unknown";
+        }
+    }
+
+    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 +863,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 +923,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 +942,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 +965,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 +1066,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 +1086,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 +1128,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 +1152,16 @@
                 }
             }
             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);
+                    } else if (isImplicitRuleId(rule.id)) {
+                        updateImplicitZenRuleNameAndDescription(rule);
                     }
                 }
             }
             if (updated) {
-                setConfigLocked(config, null, ORIGIN_SYSTEM,
+                setConfigLocked(newConfig, null, ORIGIN_SYSTEM,
                         "updateZenRulesOnLocaleChange", Process.SYSTEM_UID);
             }
         }
@@ -1170,8 +1221,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 +1358,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 +1411,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 +1429,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 +1621,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 +1664,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 +1725,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 +1768,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));
@@ -1730,11 +1789,10 @@
         pw.println(config);
     }
 
-    public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
-            throws XmlPullParserException, IOException {
-        ZenModeConfig config = ZenModeConfig.readXml(parser);
+    public boolean readXml(TypedXmlPullParser parser, boolean forRestore, int userId,
+            @Nullable BackupRestoreEventLogger logger) throws XmlPullParserException, IOException {
+        ZenModeConfig config = ZenModeConfig.readXml(parser, logger);
         String reason = "readXml";
-
         if (config != null) {
             if (forRestore) {
                 config.user = userId;
@@ -1826,22 +1884,38 @@
 
             if (DEBUG) Log.d(TAG, reason);
             synchronized (mConfigLock) {
-                setConfigLocked(config, null,
+                return setConfigLocked(config, null,
                         forRestore ? ORIGIN_RESTORE_BACKUP : ORIGIN_INIT, reason,
                         Process.SYSTEM_UID);
             }
         }
+        return false;
     }
 
-    public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
-            throws IOException {
-        synchronized (mConfigsArrayLock) {
+    public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId,
+            @Nullable BackupRestoreEventLogger logger) throws IOException {
+        synchronized (mConfigLock) {
+            int successfulWrites = 0;
+            int unsuccessfulWrites = 0;
             final int n = mConfigs.size();
             for (int i = 0; i < n; i++) {
                 if (forBackup && mConfigs.keyAt(i) != userId) {
                     continue;
                 }
-                mConfigs.valueAt(i).writeXml(out, version, forBackup);
+                try {
+                    mConfigs.valueAt(i).writeXml(out, version, forBackup, logger);
+                    successfulWrites++;
+                } catch (Exception e) {
+                    Slog.e(TAG, "failed to write config", e);
+                    unsuccessfulWrites++;
+                }
+            }
+            if (logger != null) {
+                logger.logItemsBackedUp(DATA_TYPE_ZEN_CONFIG, successfulWrites);
+                if (unsuccessfulWrites > 0) {
+                    logger.logItemsBackupFailed(DATA_TYPE_ZEN_CONFIG,
+                            unsuccessfulWrites, ERROR_XML_PARSING);
+                }
             }
         }
     }
@@ -1849,24 +1923,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 +1973,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 +2045,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 +2092,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 +2101,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 +2242,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 +2269,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 +2278,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 +2295,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 +2307,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);
@@ -2445,7 +2546,8 @@
         try {
             parser = resources.getXml(R.xml.default_zen_mode_config);
             while (parser.next() != XmlPullParser.END_DOCUMENT) {
-                final ZenModeConfig config = ZenModeConfig.readXml(XmlUtils.makeTyped(parser));
+                final ZenModeConfig config =
+                        ZenModeConfig.readXml(XmlUtils.makeTyped(parser), null);
                 if (config != null) return config;
             }
         } catch (Exception e) {
@@ -2469,7 +2571,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 +2598,7 @@
         }
     }
 
-    @GuardedBy("mConfigsArrayLock")
+    @GuardedBy("mConfigLock")
     private void ruleToProtoLocked(int user, ZenRule rule, boolean isManualRule,
             List<StatsEvent> events) {
         // Make the ID safe.
@@ -2601,7 +2703,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 +2748,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/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
index 1553618..27c4e9d 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
@@ -35,6 +35,7 @@
 
     @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName";
     @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId";
+    @VisibleForTesting static final String INSTALL_EVENT_TYPE_KEY = "installEventType";
     private static final String TAG = "BackgroundInstallControlCallbackHelper";
 
     private final Handler mHandler;
@@ -74,10 +75,14 @@
      * Invokes all registered callbacks Callbacks are processed through user provided-threads and
      * parameters are passed in via {@link BackgroundInstallControlManager} InstallEvent
      */
-    public void notifyAllCallbacks(int userId, String packageName) {
+    public void notifyAllCallbacks(
+            int userId,
+            String packageName,
+            @BackgroundInstallControlService.InstallEventType int installEventType) {
         Bundle extras = new Bundle();
         extras.putCharSequence(FLAGGED_PACKAGE_NAME_KEY, packageName);
         extras.putInt(FLAGGED_USER_ID_KEY, userId);
+        extras.putInt(INSTALL_EVENT_TYPE_KEY, installEventType);
         synchronized (mCallbacks) {
             mHandler.post(
                     () ->
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index b6daed1..20cca969 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.GET_BACKGROUND_INSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.app.Flags;
@@ -64,6 +65,8 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
@@ -76,11 +79,23 @@
  * @hide
  */
 public class BackgroundInstallControlService extends SystemService {
+    public static final int INSTALL_EVENT_TYPE_UNKNOWN = 0;
+    public static final int INSTALL_EVENT_TYPE_INSTALL = 1;
+    public static final int INSTALL_EVENT_TYPE_UNINSTALL = 2;
+
+    @IntDef(
+            value = {
+                INSTALL_EVENT_TYPE_UNKNOWN,
+                INSTALL_EVENT_TYPE_INSTALL,
+                INSTALL_EVENT_TYPE_UNINSTALL,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface InstallEventType {}
+
     private static final String TAG = "BackgroundInstallControlService";
 
     private static final String DISK_FILE_NAME = "states";
     private static final String DISK_DIR_NAME = "bic";
-
     private static final String ENFORCE_PERMISSION_ERROR_MSG =
             "User is not permitted to call service: ";
 
@@ -313,7 +328,7 @@
 
         initBackgroundInstalledPackages();
         mBackgroundInstalledPackages.add(userId, packageName);
-        mCallbackHelper.notifyAllCallbacks(userId, packageName);
+        mCallbackHelper.notifyAllCallbacks(userId, packageName, INSTALL_EVENT_TYPE_INSTALL);
         writeBackgroundInstalledPackagesToDisk();
     }
 
@@ -391,6 +406,7 @@
         initBackgroundInstalledPackages();
         mBackgroundInstalledPackages.remove(userId, packageName);
         writeBackgroundInstalledPackagesToDisk();
+        mCallbackHelper.notifyAllCallbacks(userId, packageName, INSTALL_EVENT_TYPE_UNINSTALL);
     }
 
     void handleUsageEvent(UsageEvents.Event event, int userId) {
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/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 84a5f2b..9f4b9f1 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -64,6 +64,9 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.pm.pkg.component.ParsedActivity;
+import com.android.internal.pm.pkg.component.ParsedProvider;
+import com.android.internal.pm.pkg.component.ParsedService;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -80,6 +83,8 @@
  */
 public final class BroadcastHelper {
     private static final boolean DEBUG_BROADCASTS = false;
+    private static final String PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED =
+            "android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED";
 
     private final UserManagerInternal mUmInternal;
     private final ActivityManagerInternal mAmInternal;
@@ -291,6 +296,57 @@
         return bOptions;
     }
 
+    private ArrayList<String> getAllNotExportedComponents(@NonNull AndroidPackage pkg,
+            @NonNull ArrayList<String> inputComponentNames) {
+        final ArrayList<String> outputNotExportedComponentNames = new ArrayList<>();
+        int remainingComponentCount = inputComponentNames.size();
+        for (ParsedActivity component : pkg.getReceivers()) {
+            if (inputComponentNames.contains(component.getClassName())) {
+                if (!component.isExported()) {
+                    outputNotExportedComponentNames.add(component.getClassName());
+                }
+                remainingComponentCount--;
+                if (remainingComponentCount <= 0) {
+                    return outputNotExportedComponentNames;
+                }
+            }
+        }
+        for (ParsedProvider component : pkg.getProviders()) {
+            if (inputComponentNames.contains(component.getClassName())) {
+                if (!component.isExported()) {
+                    outputNotExportedComponentNames.add(component.getClassName());
+                }
+                remainingComponentCount--;
+                if (remainingComponentCount <= 0) {
+                    return outputNotExportedComponentNames;
+                }
+            }
+        }
+        for (ParsedService component : pkg.getServices()) {
+            if (inputComponentNames.contains(component.getClassName())) {
+                if (!component.isExported()) {
+                    outputNotExportedComponentNames.add(component.getClassName());
+                }
+                remainingComponentCount--;
+                if (remainingComponentCount <= 0) {
+                    return outputNotExportedComponentNames;
+                }
+            }
+        }
+        for (ParsedActivity component : pkg.getActivities()) {
+            if (inputComponentNames.contains(component.getClassName())) {
+                if (!component.isExported()) {
+                    outputNotExportedComponentNames.add(component.getClassName());
+                }
+                remainingComponentCount--;
+                if (remainingComponentCount <= 0) {
+                    return outputNotExportedComponentNames;
+                }
+            }
+        }
+        return outputNotExportedComponentNames;
+    }
+
     private void sendPackageChangedBroadcastInternal(@NonNull String packageName,
             boolean dontKillApp,
             @NonNull ArrayList<String> componentNames,
@@ -298,10 +354,48 @@
             @Nullable String reason,
             @Nullable int[] userIds,
             @Nullable int[] instantUserIds,
-            @Nullable SparseArray<int[]> broadcastAllowList) {
-        sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
-                packageUid, reason, userIds, instantUserIds, broadcastAllowList,
-                null /* targetPackageName */, null /* requiredPermissions */);
+            @Nullable SparseArray<int[]> broadcastAllowList,
+            @NonNull AndroidPackage pkg) {
+        final boolean isForWholeApp = componentNames.contains(packageName);
+        if (isForWholeApp || !android.content.pm.Flags.reduceBroadcastsForComponentStateChanges()) {
+            sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
+                    packageUid, reason, userIds, instantUserIds, broadcastAllowList,
+                    null /* targetPackageName */, null /* requiredPermissions */);
+            return;
+        }
+        // Currently only these four components of activity, receiver, provider and service are
+        // considered to send only the broadcast to the system and the application itself when the
+        // component is not exported. In order to avoid losing to send the broadcast for other
+        // components, it gets the not exported components for these four components of activity,
+        // receiver, provider and service and the others are considered the exported components.
+        final ArrayList<String> notExportedComponentNames = getAllNotExportedComponents(pkg,
+                componentNames);
+        final ArrayList<String> exportedComponentNames = (ArrayList<String>) componentNames.clone();
+        exportedComponentNames.removeAll(notExportedComponentNames);
+
+        if (!notExportedComponentNames.isEmpty()) {
+            // Limit sending of the PACKAGE_CHANGED broadcast to only the system and the
+            // application itself when the component is not exported.
+
+            // First, send the PACKAGE_CHANGED broadcast to the system.
+            sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+                    notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
+                    broadcastAllowList, "android" /* targetPackageName */,
+                    new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED});
+
+            // Second, send the PACKAGE_CHANGED broadcast to the application itself.
+            sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+                    notExportedComponentNames, packageUid, reason, userIds, instantUserIds,
+                    broadcastAllowList, packageName /* targetPackageName */,
+                    null /* requiredPermissions */);
+        }
+
+        if (!exportedComponentNames.isEmpty()) {
+            sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp,
+                    exportedComponentNames, packageUid, reason, userIds, instantUserIds,
+                    broadcastAllowList, null /* targetPackageName */,
+                    null /* requiredPermissions */);
+        }
     }
 
     private void sendPackageChangedBroadcastWithPermissions(@NonNull String packageName,
@@ -830,7 +924,7 @@
                                      @NonNull String reason) {
         PackageStateInternal setting = snapshot.getPackageStateInternal(packageName,
                 Process.SYSTEM_UID);
-        if (setting == null) {
+        if (setting == null || setting.getPkg() == null) {
             return;
         }
         final int userId = UserHandle.getUserId(packageUid);
@@ -842,7 +936,7 @@
                 isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
         mHandler.post(() -> sendPackageChangedBroadcastInternal(
                 packageName, dontKillApp, componentNames, packageUid, reason, userIds,
-                instantUserIds, broadcastAllowList));
+                instantUserIds, broadcastAllowList, setting.getPkg()));
         mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
                 packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
     }
diff --git a/services/core/java/com/android/server/pm/SaferIntentUtils.java b/services/core/java/com/android/server/pm/SaferIntentUtils.java
index 9a7ba0f..bc36fab 100644
--- a/services/core/java/com/android/server/pm/SaferIntentUtils.java
+++ b/services/core/java/com/android/server/pm/SaferIntentUtils.java
@@ -26,7 +26,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.Overridable;
 import android.content.Intent;
@@ -45,6 +44,7 @@
 import android.util.Slog;
 
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
+import com.android.internal.pm.pkg.component.ParsedMainComponentImpl;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.IntentResolver;
 import com.android.server.LocalServices;
@@ -88,22 +88,6 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
     private static final long IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS = 229362273;
 
-    /**
-     * Intents sent from apps enabling this feature will stop resolving to components with
-     * non matching intent filters, even when explicitly setting a component name, unless the
-     * target components are in the same app as the calling app.
-     * <p>
-     * When an app registers an exported component in its manifest and adds &lt;intent-filter&gt;s,
-     * the component can be started by any intent - even those that do not match the intent filter.
-     * This has proven to be something that many developers find counterintuitive.
-     * Without checking the intent when the component is started, in some circumstances this can
-     * allow 3P apps to trigger internal-only functionality.
-     */
-    @ChangeId
-    @Overridable
-    @Disabled
-    private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
-
     @Nullable
     private static ParsedMainComponent infoToComponent(
             ComponentInfo info, ComponentResolverApi resolver, boolean isReceiver) {
@@ -277,11 +261,6 @@
                 ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM)
                 : null;
 
-        final boolean enforceMatch = Flags.enforceIntentFilterMatch()
-                && args.isChangeEnabled(ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS);
-        final boolean blockNullAction = Flags.blockNullActionIntents()
-                && args.isChangeEnabled(IntentFilter.BLOCK_NULL_ACTION_INTENTS);
-
         for (int i = resolveInfos.size() - 1; i >= 0; --i) {
             final ComponentInfo info = resolveInfos.get(i).getComponentInfo();
 
@@ -296,57 +275,65 @@
                 continue;
             }
 
-            Boolean match = null;
+            boolean enforceIntentFilter = Flags.enableIntentMatchingFlags();
+            boolean allowNullAction = false;
 
-            if (args.intent.getAction() == null) {
+            if (Flags.enableIntentMatchingFlags()) {
+                int flags = comp.getIntentMatchingFlags();
+                if (flags == 0 || (flags & ParsedMainComponentImpl.INTENT_MATCHING_FLAGS_NONE)
+                        == ParsedMainComponentImpl.INTENT_MATCHING_FLAGS_NONE
+                        || (flags
+                        & ParsedMainComponentImpl.INTENT_MATCHING_FLAGS_ENFORCE_INTENT_FILTER)
+                        == 0) {
+                    enforceIntentFilter = false;
+                }
+                if ((flags & ParsedMainComponentImpl.INTENT_MATCHING_FLAGS_ALLOW_NULL_ACTION)
+                        == ParsedMainComponentImpl.INTENT_MATCHING_FLAGS_ALLOW_NULL_ACTION) {
+                    allowNullAction = true;
+                }
+            }
+
+            boolean hasNullAction = args.intent.getAction() == null;
+            boolean intentMatchesComponent = false;
+
+            for (int j = 0, size = comp.getIntents().size(); j < size; ++j) {
+                IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter();
+                if (IntentResolver.intentMatchesFilter(
+                        intentFilter, args.intent, args.resolvedType)) {
+                    intentMatchesComponent = true;
+                    break;
+                }
+            }
+
+            boolean blockIntent = false;
+            if (enforceIntentFilter) {
+                if ((hasNullAction && !allowNullAction) || !intentMatchesComponent) {
+                    blockIntent = true;
+                }
+            }
+
+            if (hasNullAction) {
                 args.reportEvent(
-                        UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH,
-                        enforceMatch && blockNullAction);
-                if (blockNullAction) {
-                    // Skip intent filter matching if blocking null action
-                    match = false;
-                }
-            }
-
-            if (match == null) {
-                // Check if any intent filter matches
-                for (int j = 0, size = comp.getIntents().size(); j < size; ++j) {
-                    IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter();
-                    if (IntentResolver.intentMatchesFilter(
-                            intentFilter, args.intent, args.resolvedType)) {
-                        match = true;
-                        break;
-                    }
-                }
-            }
-
-            // At this point, the value `match` has the following states:
-            // null : Intent does not match any intent filter
-            // false: Null action intent detected AND blockNullAction == true
-            // true : The intent matches at least one intent filter
-
-            if (match == null) {
+                        UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH, blockIntent);
+            } else if (!intentMatchesComponent) {
                 args.reportEvent(
                         UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH,
-                        enforceMatch);
-                match = false;
+                        blockIntent);
             }
 
-            if (!match) {
-                // All non-matching intents has to be marked accordingly
-                if (Flags.enforceIntentFilterMatch()) {
-                    args.intent.addExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+            if (Flags.enforceIntentFilterMatch() && (hasNullAction || !intentMatchesComponent)) {
+                args.intent.addExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+            }
+
+            if (blockIntent) {
+                Slog.w(TAG, "Intent does not match component's intent filter: " + args.intent);
+                Slog.w(TAG, "Access blocked: " + comp.getComponentName());
+                if (DEBUG_INTENT_MATCHING) {
+                    Slog.v(TAG, "Component intent filters:");
+                    comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, "  "));
+                    Slog.v(TAG, "-----------------------------");
                 }
-                if (enforceMatch) {
-                    Slog.w(TAG, "Intent does not match component's intent filter: " + args.intent);
-                    Slog.w(TAG, "Access blocked: " + comp.getComponentName());
-                    if (DEBUG_INTENT_MATCHING) {
-                        Slog.v(TAG, "Component intent filters:");
-                        comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, "  "));
-                        Slog.v(TAG, "-----------------------------");
-                    }
-                    resolveInfos.remove(i);
-                }
+                resolveInfos.remove(i);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 6c03214..7ecfe7f 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1222,7 +1222,7 @@
         // Mark the user for removal.
         addRemovingUserIdLocked(ui.id);
         ui.partial = true;
-        ui.flags |= UserInfo.FLAG_DISABLED;
+        addUserInfoFlags(ui, UserInfo.FLAG_DISABLED);
     }
 
     /* Prunes out any partially created or partially removed users. */
@@ -1264,7 +1264,7 @@
                 if (ui.preCreated) {
                     preCreatedUsers.add(ui);
                     addRemovingUserIdLocked(ui.id);
-                    ui.flags |= UserInfo.FLAG_DISABLED;
+                    addUserInfoFlags(ui, UserInfo.FLAG_DISABLED);
                     ui.partial = true;
                 }
             }
@@ -2120,7 +2120,7 @@
                 info = getUserInfoLU(userId);
                 if (info != null && !info.isEnabled()) {
                     wasUserDisabled = true;
-                    info.flags ^= UserInfo.FLAG_DISABLED;
+                    removeUserInfoFlags(info, UserInfo.FLAG_DISABLED);
                     writeUserLP(getUserDataLU(info.id));
                 }
             }
@@ -2130,6 +2130,36 @@
         }
     }
 
+    /**
+     * This method is for monitoring flag changes on users flags and invalidate cache relevant to
+     * the change. The method add flags and invalidateOnUserInfoFlagChange for the flags which
+     * has changed.
+     * @param userInfo of existing user in mUsers list
+     * @param flags to be added to userInfo
+     */
+    private void addUserInfoFlags(UserInfo userInfo, @UserInfoFlag int flags) {
+        int diff = ~userInfo.flags & flags;
+        if (diff > 0) {
+            userInfo.flags |= diff;
+            UserManager.invalidateOnUserInfoFlagChange(diff);
+        }
+    }
+
+    /**
+     * This method is for monitoring flag changes on users flags and invalidate cache relevant to
+     * the change. The method remove flags and invalidateOnUserInfoFlagChange for the flags which
+     * has changed.
+     * @param userInfo of existing user in mUsers list
+     * @param flags to be removed from userInfo
+     */
+    private void removeUserInfoFlags(UserInfo userInfo, @UserInfoFlag int flags) {
+        int diff = userInfo.flags & flags;
+        if (diff > 0) {
+            userInfo.flags ^= diff;
+            UserManager.invalidateOnUserInfoFlagChange(diff);
+        }
+    }
+
     @Override
     public void setUserAdmin(@UserIdInt int userId) {
         checkManageUserAndAcrossUsersFullPermission("set user admin");
@@ -6245,7 +6275,7 @@
                 userData.info.guestToRemove = true;
                 // Mark it as disabled, so that it isn't returned any more when
                 // profiles are queried.
-                userData.info.flags |= UserInfo.FLAG_DISABLED;
+                addUserInfoFlags(userData.info, UserInfo.FLAG_DISABLED);
                 writeUserLP(userData);
             }
         } finally {
@@ -6390,7 +6420,7 @@
                 }
                 // Mark it as disabled, so that it isn't returned any more when
                 // profiles are queried.
-                userData.info.flags |= UserInfo.FLAG_DISABLED;
+                addUserInfoFlags(userData.info, UserInfo.FLAG_DISABLED);
                 writeUserLP(userData);
             }
 
@@ -7789,7 +7819,7 @@
                if (userInfo != null && userInfo.isEphemeral()) {
                     // Do not allow switching back to the ephemeral user again as the user is going
                     // to be deleted.
-                    userInfo.flags |= UserInfo.FLAG_DISABLED;
+                    addUserInfoFlags(userInfo, UserInfo.FLAG_DISABLED);
                     if (userInfo.isGuest()) {
                         // Indicate that the guest will be deleted after it stops.
                         userInfo.guestToRemove = true;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 2db454a..db65bf0 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -33,6 +33,7 @@
 import com.android.internal.pm.parsing.PackageParser2;
 import com.android.internal.pm.parsing.pkg.PackageImpl;
 import com.android.internal.pm.parsing.pkg.ParsedPackage;
+import com.android.internal.pm.pkg.component.AconfigFlags;
 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.pm.ApexManager;
 
@@ -41,6 +42,8 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
 public class PackageCacher implements IPackageCacher {
@@ -57,6 +60,8 @@
     @Nullable
     private final PackageParser2.Callback mCallback;
 
+    private static final AconfigFlags sAconfigFlags = ParsingPackageUtils.getAconfigFlags();
+
     public PackageCacher(File cacheDir) {
         this(cacheDir, null);
     }
@@ -136,7 +141,7 @@
      * Given a {@code packageFile} and a {@code cacheFile} returns whether the
      * cache file is up to date based on the mod-time of both files.
      */
-    private static boolean isCacheUpToDate(File packageFile, File cacheFile) {
+    private static boolean isCacheFileUpToDate(File packageFile, File cacheFile) {
         try {
             // In case packageFile is located on one of /apex mount points it's mtime will always be
             // 0. Instead, we can use mtime of the APEX file backing the corresponding mount point.
@@ -185,16 +190,36 @@
 
         try {
             // If the cache is not up to date, return null.
-            if (!isCacheUpToDate(packageFile, cacheFile)) {
+            if (!isCacheFileUpToDate(packageFile, cacheFile)) {
                 return null;
             }
 
             final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
-            ParsedPackage parsed = fromCacheEntry(bytes);
+            final ParsedPackage parsed = fromCacheEntry(bytes);
             if (!packageFile.getAbsolutePath().equals(parsed.getPath())) {
                 // Don't use this cache if the path doesn't match
                 return null;
             }
+
+            if (!android.content.pm.Flags.includeFeatureFlagsInPackageCacher()) {
+                return parsed;
+            }
+
+            final Map<String, Boolean> featureFlagState =
+                    ((PackageImpl) parsed).getFeatureFlagState();
+            if (!featureFlagState.isEmpty()) {
+                Slog.d(TAG, "Feature flags for package " + packageFile + ": " + featureFlagState);
+                for (var entry : featureFlagState.entrySet()) {
+                    final String flagPackageAndName = entry.getKey();
+                    if (!Objects.equals(sAconfigFlags.getFlagValue(flagPackageAndName),
+                            entry.getValue())) {
+                        Slog.i(TAG, "Feature flag " + flagPackageAndName + " changed for package "
+                                + packageFile + "; cached result is invalid");
+                        return null;
+                    }
+                }
+            }
+
             return parsed;
         } catch (Throwable e) {
             Slog.w(TAG, "Error reading package cache: ", e);
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index bc6a40a..07fd1cb 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1271,6 +1271,7 @@
      */
     private boolean isFixedOrUserSet(int flags) {
         return (flags & (PackageManager.FLAG_PERMISSION_USER_SET
+                | PackageManager.FLAG_PERMISSION_ONE_TIME
                 | PackageManager.FLAG_PERMISSION_USER_FIXED
                 | PackageManager.FLAG_PERMISSION_POLICY_FIXED
                 | PackageManager.FLAG_PERMISSION_SYSTEM_FIXED)) != 0;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3ab1009..2893430 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -151,6 +151,7 @@
 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;
@@ -3617,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
@@ -4053,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;
                 }
@@ -4286,6 +4361,57 @@
                 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;
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index 2e8a0c6..a928814 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -42,6 +42,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.LatencyTracker;
+import com.android.server.power.feature.PowerManagerFlags;
 
 /**
  * Used to store power related requests to every display in a
@@ -62,6 +63,8 @@
     private final DisplayManagerInternal mDisplayManagerInternal;
     private final boolean mSupportsSandman;
     private final int mGroupId;
+    private final PowerManagerFlags mFeatureFlags;
+
     /** True if DisplayManagerService has applied all the latest display states that were requested
      *  for this group. */
     private boolean mReady;
@@ -82,10 +85,15 @@
     private long mLastWakeTime;
     /** Timestamp (milliseconds since boot) of the last time the power group was put to sleep. */
     private long mLastSleepTime;
+    /** The last reason that woke the power group. */
+    private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN;
+    /** The last reason that put the power group to sleep. */
+    private @PowerManager.GoToSleepReason int mLastSleepReason =
+            PowerManager.GO_TO_SLEEP_REASON_UNKNOWN;
 
     PowerGroup(int groupId, PowerGroupListener wakefulnessListener, Notifier notifier,
             DisplayManagerInternal displayManagerInternal, int wakefulness, boolean ready,
-            boolean supportsSandman, long eventTime) {
+            boolean supportsSandman, long eventTime, PowerManagerFlags featureFlags) {
         mGroupId = groupId;
         mWakefulnessListener = wakefulnessListener;
         mNotifier = notifier;
@@ -95,10 +103,12 @@
         mSupportsSandman = supportsSandman;
         mLastWakeTime = eventTime;
         mLastSleepTime = eventTime;
+        mFeatureFlags = featureFlags;
     }
 
     PowerGroup(int wakefulness, PowerGroupListener wakefulnessListener, Notifier notifier,
-            DisplayManagerInternal displayManagerInternal, long eventTime) {
+            DisplayManagerInternal displayManagerInternal, long eventTime,
+            PowerManagerFlags featureFlags) {
         mGroupId = Display.DEFAULT_DISPLAY_GROUP;
         mWakefulnessListener = wakefulnessListener;
         mNotifier = notifier;
@@ -108,6 +118,7 @@
         mSupportsSandman = true;
         mLastWakeTime = eventTime;
         mLastSleepTime = eventTime;
+        mFeatureFlags = featureFlags;
     }
 
     long getLastWakeTimeLocked() {
@@ -138,8 +149,14 @@
                 setLastPowerOnTimeLocked(eventTime);
                 setIsPoweringOnLocked(true);
                 mLastWakeTime = eventTime;
+                if (mFeatureFlags.isPolicyReasonInDisplayPowerRequestEnabled()) {
+                    mLastWakeReason = reason;
+                }
             } else if (isInteractive(mWakefulness) && !isInteractive(newWakefulness)) {
                 mLastSleepTime = eventTime;
+                if (mFeatureFlags.isPolicyReasonInDisplayPowerRequestEnabled()) {
+                    mLastSleepReason = reason;
+                }
             }
             mWakefulness = newWakefulness;
             mWakefulnessListener.onWakefulnessChangedLocked(mGroupId, mWakefulness, eventTime,
@@ -393,37 +410,51 @@
         return false;
     }
 
-    @VisibleForTesting
-    int getDesiredScreenPolicyLocked(boolean quiescent, boolean dozeAfterScreenOff,
+    // TODO: create and use more specific policy reasons, beyond the ones that correlate to
+    // interactivity state
+    private void updateScreenPolicyLocked(boolean quiescent, boolean dozeAfterScreenOff,
             boolean bootCompleted, boolean screenBrightnessBoostInProgress,
             boolean brightWhenDozing) {
         final int wakefulness = getWakefulnessLocked();
         final int wakeLockSummary = getWakeLockSummaryLocked();
-        if (wakefulness == WAKEFULNESS_ASLEEP || quiescent) {
-            return DisplayPowerRequest.POLICY_OFF;
+        int policyReason = Display.STATE_REASON_DEFAULT_POLICY;
+        int policy = Integer.MAX_VALUE; // do not set to real policy to start with.
+        if (quiescent) {
+            policy = DisplayPowerRequest.POLICY_OFF;
+        } else if (wakefulness == WAKEFULNESS_ASLEEP) {
+            policy = DisplayPowerRequest.POLICY_OFF;
+            policyReason = sleepReasonToDisplayStateReason(mLastSleepReason);
         } else if (wakefulness == WAKEFULNESS_DOZING) {
             if ((wakeLockSummary & WAKE_LOCK_DOZE) != 0) {
-                return DisplayPowerRequest.POLICY_DOZE;
-            }
-            if (dozeAfterScreenOff) {
-                return DisplayPowerRequest.POLICY_OFF;
-            }
-            if (brightWhenDozing) {
-                return DisplayPowerRequest.POLICY_BRIGHT;
+                policy = DisplayPowerRequest.POLICY_DOZE;
+            } else if (dozeAfterScreenOff) {
+                policy = DisplayPowerRequest.POLICY_OFF;
+            } else if (brightWhenDozing) {
+                policy = DisplayPowerRequest.POLICY_BRIGHT;
             }
             // Fall through and preserve the current screen policy if not configured to
             // bright when dozing or doze after screen off.  This causes the screen off transition
             // to be skipped.
         }
 
-        if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
-                || !bootCompleted
-                || (getUserActivitySummaryLocked() & USER_ACTIVITY_SCREEN_BRIGHT) != 0
-                || screenBrightnessBoostInProgress) {
-            return DisplayPowerRequest.POLICY_BRIGHT;
+        if (policy == Integer.MAX_VALUE) { // policy is not set yet.
+            if (isInteractive(wakefulness)) {
+                policyReason = wakeReasonToDisplayStateReason(mLastWakeReason);
+            }
+            if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
+                    || !bootCompleted
+                    || (getUserActivitySummaryLocked() & USER_ACTIVITY_SCREEN_BRIGHT) != 0
+                    || screenBrightnessBoostInProgress) {
+                policy = DisplayPowerRequest.POLICY_BRIGHT;
+            } else {
+                policy = DisplayPowerRequest.POLICY_DIM;
+            }
         }
 
-        return DisplayPowerRequest.POLICY_DIM;
+        if (mFeatureFlags.isPolicyReasonInDisplayPowerRequestEnabled()) {
+            mDisplayPowerRequest.policyReason = policyReason;
+        }
+        mDisplayPowerRequest.policy = policy;
     }
 
     int getPolicyLocked() {
@@ -439,7 +470,7 @@
             boolean dozeAfterScreenOff, boolean bootCompleted,
             boolean screenBrightnessBoostInProgress, boolean waitForNegativeProximity,
             boolean brightWhenDozing) {
-        mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(quiescent, dozeAfterScreenOff,
+        updateScreenPolicyLocked(quiescent, dozeAfterScreenOff,
                 bootCompleted, screenBrightnessBoostInProgress, brightWhenDozing);
         mDisplayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
         mDisplayPowerRequest.screenBrightnessOverrideTag = overrideTag;
@@ -478,6 +509,33 @@
         return ready;
     }
 
+    /** Determines the respective display state reason for a given PowerManager WakeReason. */
+    private static int wakeReasonToDisplayStateReason(@PowerManager.WakeReason int wakeReason) {
+        switch (wakeReason) {
+            case PowerManager.WAKE_REASON_POWER_BUTTON:
+            case PowerManager.WAKE_REASON_WAKE_KEY:
+                return Display.STATE_REASON_KEY;
+            case PowerManager.WAKE_REASON_WAKE_MOTION:
+                return Display.STATE_REASON_MOTION;
+            case PowerManager.WAKE_REASON_TILT:
+                return Display.STATE_REASON_TILT;
+            default:
+                return Display.STATE_REASON_DEFAULT_POLICY;
+        }
+    }
+
+    /** Determines the respective display state reason for a given PowerManager GoToSleepReason. */
+    private static int sleepReasonToDisplayStateReason(
+            @PowerManager.GoToSleepReason int sleepReason) {
+        switch (sleepReason) {
+            case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON:
+            case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:
+                return Display.STATE_REASON_KEY;
+            default:
+                return Display.STATE_REASON_DEFAULT_POLICY;
+        }
+    }
+
     protected interface PowerGroupListener {
         /**
          * Informs the recipient about a wakefulness change of a {@link PowerGroup}.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 65f2241..3a5afac 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -790,7 +790,8 @@
                         WAKEFULNESS_AWAKE,
                         /* ready= */ false,
                         supportsSandman,
-                        mClock.uptimeMillis());
+                        mClock.uptimeMillis(),
+                        mFeatureFlags);
                 mPowerGroups.append(groupId, powerGroup);
                 onPowerGroupEventLocked(DISPLAY_GROUP_ADDED, powerGroup);
             }
@@ -1375,7 +1376,8 @@
 
             mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP,
                     new PowerGroup(WAKEFULNESS_AWAKE, mPowerGroupWakefulnessChangeListener,
-                            mNotifier, mDisplayManagerInternal, mClock.uptimeMillis()));
+                            mNotifier, mDisplayManagerInternal, mClock.uptimeMillis(),
+                            mFeatureFlags));
             DisplayGroupPowerChangeListener displayGroupPowerChangeListener =
                     new DisplayGroupPowerChangeListener();
             mDisplayManagerInternal.registerDisplayGroupListener(displayGroupPowerChangeListener);
@@ -3738,14 +3740,6 @@
     }
 
     @VisibleForTesting
-    @GuardedBy("mLock")
-    int getDesiredScreenPolicyLocked(int groupId) {
-        return mPowerGroups.get(groupId).getDesiredScreenPolicyLocked(sQuiescent,
-                mDozeAfterScreenOff, mBootCompleted,
-                mScreenBrightnessBoostInProgress, mBrightWhenDozingConfig);
-    }
-
-    @VisibleForTesting
     int getDreamsBatteryLevelDrain() {
         return mDreamsBatteryLevelDrain;
     }
@@ -4588,7 +4582,8 @@
                     WAKEFULNESS_AWAKE,
                     /* ready= */ false,
                     /* supportsSandman= */ false,
-                    mClock.uptimeMillis());
+                    mClock.uptimeMillis(),
+                    mFeatureFlags);
             mPowerGroups.append(displayGroupId, powerGroup);
         }
         mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS;
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index db57d11..4ddf0c0 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -50,6 +50,11 @@
     private final FlagState mFrameworkWakelockInfo =
             new FlagState(Flags.FLAG_FRAMEWORK_WAKELOCK_INFO, Flags::frameworkWakelockInfo);
 
+    private final FlagState mPolicyReasonInDisplayPowerRequest = new FlagState(
+            Flags.FLAG_POLICY_REASON_IN_DISPLAY_POWER_REQUEST,
+            Flags::policyReasonInDisplayPowerRequest
+    );
+
     /** Returns whether early-screen-timeout-detector is enabled on not. */
     public boolean isEarlyScreenTimeoutDetectorEnabled() {
         return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
@@ -77,6 +82,13 @@
     }
 
     /**
+     * @return Whether the wakefulness reason is populated in DisplayPowerRequest.
+     */
+    public boolean isPolicyReasonInDisplayPowerRequestEnabled() {
+        return mPolicyReasonInDisplayPowerRequest.isEnabled();
+    }
+
+    /**
      * dumps all flagstates
      * @param pw printWriter
      */
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index 8bb69ba..e27f8bb 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -33,3 +33,11 @@
     description: "Feature flag to enable statsd pulling of FrameworkWakelockInfo atoms"
     bug: "352602149"
 }
+
+flag {
+    name: "policy_reason_in_display_power_request"
+    namespace: "wear_frameworks"
+    description: "Whether the policy reason is populted in DisplayPowerRequest."
+    bug: "364349703"
+    is_fixed_read_only: true
+}
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..1260eee 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;
@@ -32,40 +33,71 @@
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.provider.Settings;
+import android.security.advancedprotection.AdvancedProtectionFeature;
 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 com.android.server.security.advancedprotection.features.AdvancedProtectionProvider;
 
 import java.io.FileDescriptor;
 import java.util.ArrayList;
+import java.util.List;
 
 /** @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 living with 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<>();
+    // For tracking only - not called on state change
+    private final ArrayList<AdvancedProtectionProvider> mProviders = new ArrayList<>();
 
     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) {
+        // Empty until features are added.
+        // Examples:
+        // mHooks.add(new SideloadingAdvancedProtectionHook(mContext, enabled));
+        // mProviders.add(new WifiAdvancedProtectionProvider());
+    }
+
+    // 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, @Nullable AdvancedProtectionProvider provider) {
         super(permissionEnforcer);
+        mContext = context;
         mStore = store;
         mHandler = new AdvancedProtectionHandler(looper);
+        if (hook != null) {
+            mHooks.add(hook);
+        }
+
+        if (provider != null) {
+            mProviders.add(provider);
+        }
     }
 
     @Override
@@ -100,8 +132,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 +150,7 @@
                 if (enabled != isAdvancedProtectionEnabledInternal()) {
                     mStore.store(enabled);
                     sendModeChanged(enabled);
+                    Slog.i(TAG, "Advanced protection is " + (enabled ? "enabled" : "disabled"));
                 }
             }
         } finally {
@@ -126,6 +159,25 @@
     }
 
     @Override
+    @EnforcePermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE)
+    public List<AdvancedProtectionFeature> getAdvancedProtectionFeatures() {
+        getAdvancedProtectionFeatures_enforcePermission();
+        List<AdvancedProtectionFeature> features = new ArrayList<>();
+        for (int i = 0; i < mProviders.size(); i++) {
+            features.addAll(mProviders.get(i).getFeatures());
+        }
+
+        for (int i = 0; i < mHooks.size(); i++) {
+            AdvancedProtectionHook hook = mHooks.get(i);
+            if (hook.isAvailable()) {
+                features.add(hook.getFeature());
+            }
+        }
+
+        return features;
+    }
+
+    @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, @NonNull String[] args, ShellCallback callback,
             @NonNull ResultReceiver resultReceiver) {
@@ -156,6 +208,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 +268,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..f82db96
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionHook.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.security.advancedprotection.features;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.security.advancedprotection.AdvancedProtectionFeature;
+
+/** @hide */
+public abstract class AdvancedProtectionHook {
+    /** Called on boot phase PHASE_SYSTEM_SERVICES_READY */
+    public AdvancedProtectionHook(@NonNull Context context, boolean enabled) {}
+    /** The feature this hook provides */
+    @NonNull
+    public abstract AdvancedProtectionFeature getFeature();
+    /** 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/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
similarity index 64%
copy from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
copy to services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
index 83cbc52..ed451f1 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
@@ -13,10 +13,15 @@
  * 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.advancedprotection.features;
+
+import android.security.advancedprotection.AdvancedProtectionFeature;
+
+import java.util.List;
+
+/** @hide */
+public abstract class AdvancedProtectionProvider {
+    /** The list of features provided */
+    public abstract List<AdvancedProtectionFeature> getFeatures();
 }
diff --git a/services/core/java/com/android/server/security/forensic/DataAggregator.java b/services/core/java/com/android/server/security/forensic/DataAggregator.java
index 0079818..cc473ca 100644
--- a/services/core/java/com/android/server/security/forensic/DataAggregator.java
+++ b/services/core/java/com/android/server/security/forensic/DataAggregator.java
@@ -16,6 +16,7 @@
 
 package com.android.server.security.forensic;
 
+import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -38,11 +39,14 @@
     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(ForensicService forensicService) {
+
+    public DataAggregator(Context context, ForensicService forensicService) {
         mForensicService = forensicService;
+        mContext = context;
         mDataSources = new ArrayList<DataSource>();
     }
 
@@ -58,6 +62,8 @@
      */
     // TODO: Add the corresponding data sources
     public boolean initialize() {
+        SecurityLogSource securityLogSource = new SecurityLogSource(mContext, this);
+        mDataSources.add(securityLogSource);
         return true;
     }
 
@@ -93,6 +99,9 @@
      */
     public void disable() {
         mHandler.obtainMessage(MSG_DISABLE).sendToTarget();
+        for (DataSource ds : mDataSources) {
+            ds.disable();
+        }
     }
 
     private void onNewSingleData(ForensicEvent event) {
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 53b07c0..01f630b 100644
--- a/services/core/java/com/android/server/security/forensic/ForensicService.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -332,7 +332,7 @@
 
         @Override
         public DataAggregator getDataAggregator(ForensicService forensicService) {
-            return new DataAggregator(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/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 457196b..465ac2f 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -21,6 +21,7 @@
 import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE;
 
 import android.Manifest;
+import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -1896,8 +1897,11 @@
             }
         }
 
+        @EnforcePermission(Manifest.permission.ACCESS_FINE_LOCATION)
         @Override
         public boolean isInSignificantPlace() {
+            super.isInSignificantPlace_enforcePermission();
+
             if (android.security.Flags.significantPlaces()) {
                 mSignificantPlaceServiceWatcher.runOnBinder(
                         binder -> ISignificantPlaceProvider.Stub.asInterface(binder)
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 0962319..38bc026 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -115,11 +115,18 @@
      */
     private int mPriority;
 
+    /**
+     * If resource holder retains ownership of the resource in a challenge scenario then value is
+     * true.
+     */
+    private boolean mResourceHolderRetain;
+
     private ClientProfile(Builder builder) {
         this.mId = builder.mId;
         this.mTvInputSessionId = builder.mTvInputSessionId;
         this.mUseCase = builder.mUseCase;
         this.mProcessId = builder.mProcessId;
+        this.mResourceHolderRetain = builder.mResourceHolderRetain;
     }
 
     public int getId() {
@@ -139,6 +146,14 @@
     }
 
     /**
+     * Returns true when the resource holder retains ownership of the resource in a challenge
+     * scenario.
+     */
+    public boolean shouldResourceHolderRetain() {
+        return mResourceHolderRetain;
+    }
+
+    /**
      * If the client priority is overwrttien.
      */
     public boolean isPriorityOverwritten() {
@@ -180,6 +195,19 @@
     }
 
     /**
+     * Determines whether the resource holder retains ownership of the resource during a challenge
+     * scenario, when both resource holder and resource challenger have same processId and same
+     * priority.
+     *
+     * @param resourceHolderRetain Set to true to allow the resource holder to retain ownership, or
+     *     false (or resourceHolderRetain not set at all) to allow the resource challenger to
+     *     acquire the resource. If not explicitly set, resourceHolderRetain is set to false.
+     */
+    public void setResourceHolderRetain(boolean resourceHolderRetain) {
+        mResourceHolderRetain = resourceHolderRetain;
+    }
+
+    /**
      * Set when the client starts to use a frontend.
      *
      * @param frontendHandle being used.
@@ -361,6 +389,7 @@
         private String mTvInputSessionId;
         private int mUseCase;
         private int mProcessId;
+        private boolean mResourceHolderRetain = false;
 
         Builder(int id) {
             this.mId = id;
@@ -397,6 +426,18 @@
         }
 
         /**
+         * Builder for {@link ClientProfile}.
+         *
+         * @param resourceHolderRetain the determining factor for resource ownership during
+         *     challenger scenario. The default behavior favors the resource challenger and grants
+         *     them ownership of the resource if resourceHolderRetain is not explicitly set to true.
+         */
+        public Builder resourceHolderRetain(boolean resourceHolderRetain) {
+            this.mResourceHolderRetain = resourceHolderRetain;
+            return this;
+        }
+
+        /**
           * Build a {@link ClientProfile}.
           *
           * @return {@link ClientProfile}.
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index c5b6bbf..5ae8c11 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.tv.tunerresourcemanager;
 
+import static android.media.tv.flags.Flags.setResourceHolderRetain;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -229,6 +231,14 @@
         }
 
         @Override
+        public void setResourceHolderRetain(int clientId, boolean resourceHolderRetain) {
+            enforceTrmAccessPermission("setResourceHolderRetain");
+            synchronized (mLock) {
+                getClientProfile(clientId).setResourceHolderRetain(resourceHolderRetain);
+            }
+        }
+
+        @Override
         public boolean isLowestPriority(int clientId, int frontendType)
                 throws RemoteException {
             enforceTrmAccessPermission("isLowestPriority");
@@ -1066,8 +1076,10 @@
             // request client has higher priority.
             if (inUseLowestPriorityFrontend != null
                     && ((requestClient.getPriority() > currentLowestPriority)
-                    || ((requestClient.getPriority() == currentLowestPriority)
-                    && isRequestFromSameProcess))) {
+                            || ((requestClient.getPriority() == currentLowestPriority)
+                                    && isRequestFromSameProcess
+                                    && !(setResourceHolderRetain()
+                                            && requestClient.shouldResourceHolderRetain())))) {
                 frontendHandle[0] = inUseLowestPriorityFrontend.getHandle();
                 reclaimOwnerId[0] = inUseLowestPriorityFrontend.getOwnerClientId();
                 return true;
@@ -1249,9 +1261,11 @@
             // When all the resources are occupied, grant the lowest priority resource if the
             // request client has higher priority.
             if (inUseLowestPriorityLnb != null
-                    && ((requestClient.getPriority() > currentLowestPriority) || (
-                    (requestClient.getPriority() == currentLowestPriority)
-                        && isRequestFromSameProcess))) {
+                    && ((requestClient.getPriority() > currentLowestPriority)
+                            || ((requestClient.getPriority() == currentLowestPriority)
+                                    && isRequestFromSameProcess
+                                    && !(setResourceHolderRetain()
+                                            && requestClient.shouldResourceHolderRetain())))) {
                 lnbHandle[0] = inUseLowestPriorityLnb.getHandle();
                 reclaimOwnerId[0] = inUseLowestPriorityLnb.getOwnerClientId();
                 return true;
@@ -1335,8 +1349,10 @@
             // request client has higher priority.
             if (lowestPriorityOwnerId != INVALID_CLIENT_ID
                     && ((requestClient.getPriority() > currentLowestPriority)
-                    || ((requestClient.getPriority() == currentLowestPriority)
-                    && isRequestFromSameProcess))) {
+                            || ((requestClient.getPriority() == currentLowestPriority)
+                                    && isRequestFromSameProcess
+                                    && !(setResourceHolderRetain()
+                                            && requestClient.shouldResourceHolderRetain())))) {
                 casSessionHandle[0] = cas.getHandle();
                 reclaimOwnerId[0] = lowestPriorityOwnerId;
                 return true;
@@ -1420,8 +1436,10 @@
             // request client has higher priority.
             if (lowestPriorityOwnerId != INVALID_CLIENT_ID
                     && ((requestClient.getPriority() > currentLowestPriority)
-                    || ((requestClient.getPriority() == currentLowestPriority)
-                    && isRequestFromSameProcess))) {
+                            || ((requestClient.getPriority() == currentLowestPriority)
+                                    && isRequestFromSameProcess
+                                    && !(setResourceHolderRetain()
+                                            && requestClient.shouldResourceHolderRetain())))) {
                 ciCamHandle[0] = ciCam.getHandle();
                 reclaimOwnerId[0] = lowestPriorityOwnerId;
                 return true;
@@ -1655,9 +1673,11 @@
             // When all the resources are occupied, grant the lowest priority resource if the
             // request client has higher priority.
             if (inUseLowestPriorityDemux != null
-                    && ((requestClient.getPriority() > currentLowestPriority) || (
-                    (requestClient.getPriority() == currentLowestPriority)
-                        && isRequestFromSameProcess))) {
+                    && ((requestClient.getPriority() > currentLowestPriority)
+                            || ((requestClient.getPriority() == currentLowestPriority)
+                                    && isRequestFromSameProcess
+                                    && !(setResourceHolderRetain()
+                                            && requestClient.shouldResourceHolderRetain())))) {
                 demuxHandle[0] = inUseLowestPriorityDemux.getHandle();
                 reclaimOwnerId[0] = inUseLowestPriorityDemux.getOwnerClientId();
                 return true;
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/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index baf84cf..3392d03 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -436,6 +436,17 @@
             return mPrivilegedPackages.keySet();
         }
 
+        /** Returns all subscription groups */
+        @NonNull
+        public Set<ParcelUuid> getAllSubscriptionGroups() {
+            final Set<ParcelUuid> subGroups = new ArraySet<>();
+            for (SubscriptionInfo subInfo : mSubIdToInfoMap.values()) {
+                subGroups.add(subInfo.getGroupUuid());
+            }
+
+            return subGroups;
+        }
+
         /** Checks if the provided package is carrier privileged for the specified sub group. */
         public boolean packageHasPermissionsForSubscriptionGroup(
                 @NonNull ParcelUuid subGrp, @NonNull String packageName) {
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index a492a72..6ce8685 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.net.IpSecTransformState;
 import android.net.vcn.FeatureFlags;
 import android.net.vcn.FeatureFlagsImpl;
 import android.os.Looper;
@@ -70,19 +69,6 @@
         return mIsInTestMode;
     }
 
-    public boolean isFlagIpSecTransformStateEnabled() {
-        // TODO: b/328844044: Ideally this code should gate the behavior by checking the
-        // android.net.platform.flags.ipsec_transform_state flag but that flag is not accessible
-        // right now. We should either update the code when the flag is accessible or remove the
-        // legacy behavior after VIC SDK finalization
-        try {
-            new IpSecTransformState.Builder();
-            return true;
-        } catch (Exception e) {
-            return false;
-        }
-    }
-
     @NonNull
     public FeatureFlags getFeatureFlags() {
         return mFeatureFlags;
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 189b608..2d3bc84 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1912,8 +1912,7 @@
                 // Transforms do not need to be persisted; the IkeSession will keep them alive
                 mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
 
-                if (direction == IpSecManager.DIRECTION_IN
-                        && mVcnContext.isFlagIpSecTransformStateEnabled()) {
+                if (direction == IpSecManager.DIRECTION_IN) {
                     mUnderlyingNetworkController.updateInboundTransform(mUnderlying, transform);
                 }
 
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 6f1e15b..16ab51e 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -148,12 +148,6 @@
 
         Objects.requireNonNull(deps, "Missing deps");
 
-        if (!vcnContext.isFlagIpSecTransformStateEnabled()) {
-            // Caller error
-            logWtf("ipsecTransformState flag disabled");
-            throw new IllegalAccessException("ipsecTransformState flag disabled");
-        }
-
         mHandler = new Handler(getVcnContext().getLooper());
 
         mPowerManager = getVcnContext().getContext().getSystemService(PowerManager.class);
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 0b9b677..3eeeece 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -204,10 +204,8 @@
         List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
         mCellBringupCallbacks.clear();
 
-        if (mVcnContext.isFlagIpSecTransformStateEnabled()) {
-            for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
-                evaluator.close();
-            }
+        for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
+            evaluator.close();
         }
 
         mUnderlyingNetworkRecords.clear();
@@ -429,10 +427,7 @@
         if (oldSnapshot
                 .getAllSubIdsInGroup(mSubscriptionGroup)
                 .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) {
-
-            if (mVcnContext.isFlagIpSecTransformStateEnabled()) {
-                reevaluateNetworks();
-            }
+            reevaluateNetworks();
             return;
         }
         registerOrUpdateNetworkRequests();
@@ -445,11 +440,6 @@
      */
     public void updateInboundTransform(
             @NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) {
-        if (!mVcnContext.isFlagIpSecTransformStateEnabled()) {
-            logWtf("#updateInboundTransform: unexpected call; flags missing");
-            return;
-        }
-
         Objects.requireNonNull(currentNetwork, "currentNetwork is null");
         Objects.requireNonNull(transform, "transform is null");
 
@@ -572,10 +562,7 @@
 
         @Override
         public void onLost(@NonNull Network network) {
-            if (mVcnContext.isFlagIpSecTransformStateEnabled()) {
-                mUnderlyingNetworkRecords.get(network).close();
-            }
-
+            mUnderlyingNetworkRecords.get(network).close();
             mUnderlyingNetworkRecords.remove(network);
 
             reevaluateNetworks();
@@ -648,11 +635,6 @@
     class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback {
         @Override
         public void onEvaluationResultChanged() {
-            if (!mVcnContext.isFlagIpSecTransformStateEnabled()) {
-                logWtf("#onEvaluationResultChanged: unexpected call; flags missing");
-                return;
-            }
-
             mVcnContext.ensureRunningOnLooperThread();
             reevaluateNetworks();
         }
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
index 448a7eb..08be11e 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -102,17 +102,15 @@
         updatePriorityClass(
                 underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
 
-        if (isIpSecPacketLossDetectorEnabled()) {
-            try {
-                mMetricMonitors.add(
-                        mDependencies.newIpSecPacketLossDetector(
-                                mVcnContext,
-                                mNetworkRecordBuilder.getNetwork(),
-                                carrierConfig,
-                                new MetricMonitorCallbackImpl()));
-            } catch (IllegalAccessException e) {
-                // No action. Do not add anything to mMetricMonitors
-            }
+        try {
+            mMetricMonitors.add(
+                    mDependencies.newIpSecPacketLossDetector(
+                            mVcnContext,
+                            mNetworkRecordBuilder.getNetwork(),
+                            carrierConfig,
+                            new MetricMonitorCallbackImpl()));
+        } catch (IllegalAccessException e) {
+            // No action. Do not add anything to mMetricMonitors
         }
     }
 
@@ -188,22 +186,12 @@
         }
     }
 
-    private boolean isIpSecPacketLossDetectorEnabled() {
-        return isIpSecPacketLossDetectorEnabled(mVcnContext);
-    }
-
-    private static boolean isIpSecPacketLossDetectorEnabled(VcnContext vcnContext) {
-        return vcnContext.isFlagIpSecTransformStateEnabled();
-    }
-
     /** Get the comparator for UnderlyingNetworkEvaluator */
     public static Comparator<UnderlyingNetworkEvaluator> getComparator(VcnContext vcnContext) {
         return (left, right) -> {
-            if (isIpSecPacketLossDetectorEnabled(vcnContext)) {
-                if (left.mIsPenalized != right.mIsPenalized) {
-                    // A penalized network should have lower priority which means a larger index
-                    return left.mIsPenalized ? 1 : -1;
-                }
+            if (left.mIsPenalized != right.mIsPenalized) {
+                // A penalized network should have lower priority which means a larger index
+                return left.mIsPenalized ? 1 : -1;
             }
 
             final int leftIndex = left.mPriorityClass;
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
index d0d6071..32a3227 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
@@ -20,6 +20,7 @@
 import android.os.Trace;
 import android.os.VibrationEffect;
 import android.os.vibrator.Flags;
+import android.os.vibrator.PwlePoint;
 import android.os.vibrator.PwleSegment;
 import android.os.vibrator.VibrationEffectSegment;
 import android.util.Slog;
@@ -57,7 +58,7 @@
             // 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);
+            List<PwlePoint> pwles = unrollPwleSegments(effect, segmentIndex, limit);
 
             if (pwles.isEmpty()) {
                 Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposeEnvelopeStep: "
@@ -70,7 +71,7 @@
                 Slog.d(VibrationThread.TAG, "Compose " + pwles + " PWLEs on vibrator "
                         + controller.getVibratorInfo().getId());
             }
-            PwleSegment[] pwlesArray = pwles.toArray(new PwleSegment[pwles.size()]);
+            PwlePoint[] pwlesArray = pwles.toArray(new PwlePoint[pwles.size()]);
             long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
             handleVibratorOnResult(vibratorOnResult);
             getVibration().stats.reportComposePwle(vibratorOnResult, pwlesArray);
@@ -82,9 +83,9 @@
         }
     }
 
-    private List<PwleSegment> unrollPwleSegments(VibrationEffect.Composed effect, int startIndex,
+    private List<PwlePoint> unrollPwleSegments(VibrationEffect.Composed effect, int startIndex,
             int limit) {
-        List<PwleSegment> segments = new ArrayList<>(limit);
+        List<PwlePoint> pwlePoints = new ArrayList<>(limit);
         float bestBreakAmplitude = 1;
         int bestBreakPosition = limit; // Exclusive index.
 
@@ -93,7 +94,7 @@
 
         // 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++) {
+        for (int i = startIndex; pwlePoints.size() < limit; i++) {
             if (i == segmentCount) {
                 if (repeatIndex >= 0) {
                     i = repeatIndex;
@@ -104,12 +105,20 @@
             }
             VibrationEffectSegment segment = effect.getSegments().get(i);
             if (segment instanceof PwleSegment pwleSegment) {
-                segments.add(pwleSegment);
+                if (pwlePoints.isEmpty()) {
+                    // The initial state is defined by the starting amplitude and frequency of the
+                    // first PwleSegment. The time parameter is set to zero to indicate this is
+                    // the initial condition without any ramp up time.
+                    pwlePoints.add(new PwlePoint(pwleSegment.getStartAmplitude(),
+                            pwleSegment.getStartFrequencyHz(), /*timeMillis=*/ 0));
+                }
+                pwlePoints.add(new PwlePoint(pwleSegment.getEndAmplitude(),
+                        pwleSegment.getEndFrequencyHz(), (int) pwleSegment.getDuration()));
 
-                if (isBetterBreakPosition(segments, bestBreakAmplitude, limit)) {
+                if (isBetterBreakPosition(pwlePoints, 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.
+                    bestBreakPosition = pwlePoints.size(); // Break after this pwle ends.
                 }
             } else {
                 // First non-pwle segment, stop collecting pwles.
@@ -117,21 +126,21 @@
             }
         }
 
-        return segments.size() > limit
+        return pwlePoints.size() > limit
                 // Remove excessive segments, using the best breaking position recorded.
-                ? segments.subList(0, bestBreakPosition)
+                ? pwlePoints.subList(0, bestBreakPosition)
                 // Return all collected pwle segments.
-                : segments;
+                : pwlePoints;
     }
 
     /**
      * 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,
+    private boolean isBetterBreakPosition(List<PwlePoint> segments,
             float currentBestBreakAmplitude, int limit) {
-        PwleSegment lastSegment = segments.get(segments.size() - 1);
-        float breakAmplitudeCandidate = lastSegment.getEndAmplitude();
+        PwlePoint lastSegment = segments.get(segments.size() - 1);
+        float breakAmplitudeCandidate = lastSegment.getAmplitude();
         int breakPositionCandidate = segments.size();
 
         if (breakPositionCandidate > limit) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationStats.java b/services/core/java/com/android/server/vibrator/VibrationStats.java
index bc4dbe7..de423f0 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStats.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStats.java
@@ -22,7 +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.PwlePoint;
 import android.os.vibrator.RampSegment;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -294,18 +294,22 @@
     }
 
     /** Report a call to vibrator method to trigger a vibration as a PWLE. */
-    void reportComposePwle(long halResult, PwleSegment[] segments) {
+    void reportComposePwle(long halResult, PwlePoint[] pwlePoints) {
         mVibratorComposePwleCount++;
-        mVibrationPwleTotalSize += segments.length;
+        mVibrationPwleTotalSize += pwlePoints.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();
+            for (int i = 0; i < pwlePoints.length - 1; i++) {
+                PwlePoint current = pwlePoints[i];
+                PwlePoint next = pwlePoints[i + 1];
+
+                if (current.getAmplitude() == 0 && next.getAmplitude() == 0) {
+                    halResult -= next.getTimeMillis();
                 }
             }
+
             if (halResult > 0) {
                 mVibratorOnTotalDurationMillis += (int) halResult;
             }
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index f78bff8..acb31ce 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -30,7 +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.PwlePoint;
 import android.os.vibrator.RampSegment;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -415,21 +415,21 @@
     }
 
     /**
-     * Plays a composition of pwle v2 primitives, using {@code vibrationId} for completion callback
+     * Plays a composition of pwle v2 points, 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) {
+    public long on(PwlePoint[] pwlePoints, 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);
+                long duration = mNativeWrapper.composePwleV2(pwlePoints, vibrationId);
                 if (duration > 0) {
                     mCurrentAmplitude = -1;
                     updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -562,7 +562,7 @@
         private static native long performPwleEffect(long nativePtr, RampSegment[] effect,
                 int braking, long vibrationId);
 
-        private static native long performPwleV2Effect(long nativePtr, PwleSegment[] effect,
+        private static native long performPwleV2Effect(long nativePtr, PwlePoint[] effect,
                 long vibrationId);
 
         private static native void setExternalControl(long nativePtr, boolean enabled);
@@ -631,9 +631,9 @@
             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);
+        /** Turns vibrator on to perform PWLE effect composed of given points. */
+        public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId) {
+            return performPwleV2Effect(mNativePtr, pwlePoints, vibrationId);
         }
 
         /** Enabled the device vibrator to be controlled by another service. */
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java
index 15f86e9..f09b0a1c6 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperData.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wallpaper;
 
+import static android.app.Flags.liveWallpaperContentHandling;
 import static android.app.WallpaperManager.FLAG_LOCK;
 import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
 
@@ -25,10 +26,12 @@
 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_LOCK_ORIG;
 import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
 
+import android.annotation.NonNull;
 import android.app.IWallpaperManagerCallback;
 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 +80,8 @@
 
     /**
      * The component name of the currently set live wallpaper.
+     *
+     * @deprecated
      */
     private ComponentName mWallpaperComponent;
 
@@ -146,6 +151,7 @@
         UNKNOWN,
         CONNECT_LOCKED,
         CONNECTION_TRY_TO_REBIND,
+        FALLBACK_DEFAULT_MISSING,
         INITIALIZE_FALLBACK,
         PACKAGE_UPDATE_FINISHED,
         RESTORE_SETTINGS_LIVE_FAILURE,
@@ -179,6 +185,9 @@
      */
     int mOrientationWhenSet = ORIENTATION_UNKNOWN;
 
+    /** Description of the current wallpaper */
+    private WallpaperDescription mDescription = new WallpaperDescription.Builder().build();
+
     WallpaperData(int userId, @SetWallpaperFlags int wallpaperType) {
         this.userId = userId;
         this.mWhich = wallpaperType;
@@ -206,6 +215,9 @@
         this.primaryColors = source.primaryColors;
         this.mWallpaperDimAmount = source.mWallpaperDimAmount;
         this.connection = source.connection;
+        if (liveWallpaperContentHandling()) {
+            this.setDescription(source.getDescription());
+        }
         if (this.connection != null) {
             this.connection.mWallpaper = this;
         }
@@ -230,14 +242,40 @@
         return result;
     }
 
-    ComponentName getComponent() {
-        return mWallpaperComponent;
+    @NonNull ComponentName getComponent() {
+        if (liveWallpaperContentHandling()) {
+            return mDescription.getComponent();
+        } else {
+            return mWallpaperComponent;
+        }
     }
 
-    void setComponent(ComponentName componentName) {
+    void setComponent(@NonNull ComponentName componentName) {
+        if (liveWallpaperContentHandling()) {
+            throw new IllegalStateException(
+                    "Use \"setDescription\" when content handling is enabled");
+        }
         this.mWallpaperComponent = componentName;
     }
 
+    @NonNull WallpaperDescription getDescription() {
+        return mDescription;
+    }
+
+    void setDescription(@NonNull WallpaperDescription description) {
+        if (!liveWallpaperContentHandling()) {
+            throw new IllegalStateException(
+                    "Use \"setContent\" when content handling is disabled");
+        }
+        if (description == null) {
+            throw new IllegalArgumentException("WallpaperDescription must not be null");
+        }
+        if (description.getComponent() == null) {
+            throw new IllegalArgumentException("WallpaperDescription component must not be null");
+        }
+        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..17a254a 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;
@@ -177,15 +180,8 @@
             success = true;
         } catch (FileNotFoundException e) {
             Slog.w(TAG, "no current wallpaper -- first boot?");
-        } catch (NullPointerException e) {
-            Slog.w(TAG, "failed parsing " + file + " " + e);
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "failed parsing " + file + " " + e);
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, "failed parsing " + file + " " + e);
-        } catch (IOException e) {
-            Slog.w(TAG, "failed parsing " + file + " " + e);
-        } catch (IndexOutOfBoundsException e) {
+        } catch (NullPointerException | NumberFormatException | XmlPullParserException
+                 | IOException | IndexOutOfBoundsException e) {
             Slog.w(TAG, "failed parsing " + file + " " + e);
         }
         IoUtils.closeQuietly(stream);
@@ -194,9 +190,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,27 +248,14 @@
                         parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints);
                     }
 
-                    String comp = parser.getAttributeValue(null, "component");
-                    if (removeNextWallpaperComponent()) {
-                        wallpaperToParse.setComponent(comp != null
-                                ? ComponentName.unflattenFromString(comp)
-                                : null);
-                        if (wallpaperToParse.getComponent() == null
-                                || "android".equals(wallpaperToParse.getComponent()
-                                .getPackageName())) {
-                            wallpaperToParse.setComponent(mImageWallpaper);
-                        }
-                    } else {
-                        wallpaperToParse.nextWallpaperComponent = comp != null
-                                ? ComponentName.unflattenFromString(comp)
-                                : null;
-                        if (wallpaperToParse.nextWallpaperComponent == null
-                                || "android".equals(wallpaperToParse.nextWallpaperComponent
-                                .getPackageName())) {
-                            wallpaperToParse.nextWallpaperComponent = mImageWallpaper;
+                    ComponentName comp = parseComponentName(parser);
+                    if (!liveWallpaperContentHandling()) {
+                        if (removeNextWallpaperComponent()) {
+                            wallpaperToParse.setComponent(comp);
+                        } else {
+                            wallpaperToParse.nextWallpaperComponent = comp;
                         }
                     }
-
                     if (multiCrop()) {
                         parseWallpaperAttributes(parser, wallpaperToParse, keepDimensionHints);
                     }
@@ -290,6 +280,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 +333,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 +376,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 +463,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 +639,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))) {
@@ -692,9 +739,9 @@
 
     private static List<Pair<Integer, String>> screenDimensionPairs() {
         return List.of(
-                new Pair<>(WallpaperManager.PORTRAIT, "Portrait"),
-                new Pair<>(WallpaperManager.LANDSCAPE, "Landscape"),
-                new Pair<>(WallpaperManager.SQUARE_PORTRAIT, "SquarePortrait"),
-                new Pair<>(WallpaperManager.SQUARE_LANDSCAPE, "SquareLandscape"));
+                new Pair<>(WallpaperManager.ORIENTATION_PORTRAIT, "Portrait"),
+                new Pair<>(WallpaperManager.ORIENTATION_LANDSCAPE, "Landscape"),
+                new Pair<>(WallpaperManager.ORIENTATION_SQUARE_PORTRAIT, "SquarePortrait"),
+                new Pair<>(WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE, "SquareLandscape"));
     }
 }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index da9a676..5cff37a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -21,6 +21,7 @@
 import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.Flags.fixWallpaperChanged;
+import static android.app.Flags.liveWallpaperContentHandling;
 import static android.app.Flags.removeNextWallpaperComponent;
 import static android.app.WallpaperManager.COMMAND_REAPPLY;
 import static android.app.WallpaperManager.FLAG_LOCK;
@@ -66,6 +67,8 @@
 import android.app.WallpaperManager;
 import android.app.WallpaperManager.SetWallpaperFlags;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.app.wallpaper.WallpaperDescription;
+import android.app.wallpaper.WallpaperInstance;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -849,16 +852,24 @@
             final DisplayData wpdData =
                     mWallpaperDisplayHelper.getDisplayDataOrCreate(mDisplayId);
             try {
-                connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
-                        wpdData.mWidth, wpdData.mHeight,
-                        wpdData.mPadding, mDisplayId, wallpaper.mWhich, connection.mInfo);
+                if (liveWallpaperContentHandling()) {
+                    connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
+                            wpdData.mWidth, wpdData.mHeight,
+                            wpdData.mPadding, mDisplayId, wallpaper.mWhich, connection.mInfo,
+                            wallpaper.getDescription());
+                } else {
+                    connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
+                            wpdData.mWidth, wpdData.mHeight,
+                            wpdData.mPadding, mDisplayId, wallpaper.mWhich, connection.mInfo,
+                            /* description= */ null);
+                }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failed attaching wallpaper on display", e);
                 if (wallpaper != null && !wallpaper.wallpaperUpdating
                         && connection.getConnectedEngineSize() == 0) {
                     wallpaper.mBindSource = BindSource.CONNECT_LOCKED;
-                    bindWallpaperComponentLocked(null /* componentName */, false /* force */,
-                            false /* fromUser */, wallpaper, null /* reply */);
+                    bindWallpaperComponentLocked(null, false /* force */, false /* fromUser */,
+                            wallpaper, null /* reply */);
                 }
             }
             t.traceEnd();
@@ -1324,7 +1335,11 @@
                             if (DEBUG) {
                                 Slog.v(TAG, "static system+lock to system success");
                             }
-                            lockWp.setComponent(mOriginalSystem.getComponent());
+                            if (liveWallpaperContentHandling()) {
+                                lockWp.setDescription(mOriginalSystem.getDescription());
+                            } else {
+                                lockWp.setComponent(mOriginalSystem.getComponent());
+                            }
                             lockWp.connection = mOriginalSystem.connection;
                             lockWp.connection.mWallpaper = lockWp;
                             mOriginalSystem.mWhich = FLAG_LOCK;
@@ -1391,7 +1406,7 @@
                             Slog.i(TAG, "Wallpaper " + wpService + " update has finished");
                         }
                         wallpaper.wallpaperUpdating = false;
-                        clearWallpaperComponentLocked(wallpaper);
+                        detachWallpaperLocked(wallpaper);
                         wallpaper.mBindSource = BindSource.PACKAGE_UPDATE_FINISHED;
                         if (!bindWallpaperComponentLocked(wpService, false, false,
                                 wallpaper, null)) {
@@ -1905,6 +1920,23 @@
             if ((wallpaper.mWhich & FLAG_SYSTEM) != 0) mHomeWallpaperWaitingForUnlock = false;
             if ((wallpaper.mWhich & FLAG_LOCK) != 0) mLockWallpaperWaitingForUnlock = false;
 
+            if (liveWallpaperContentHandling()) {
+                final WallpaperDescription description = wallpaper.getDescription();
+                if (!bindWallpaperDescriptionLocked(description, true, false, wallpaper, reply)) {
+                    // We failed to bind the desired wallpaper, but that might
+                    // happen if the wallpaper isn't direct-boot aware
+                    ServiceInfo si = null;
+                    try {
+                        si = mIPackageManager.getServiceInfo(description.getComponent(),
+                                PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failure starting previous wallpaper; clearing", e);
+                    }
+                    onSwitchWallpaperFailLocked(wallpaper, reply, si);
+                }
+                return;
+            }
+
             final ComponentName cname;
             if (removeNextWallpaperComponent()) {
                 cname = wallpaper.getComponent();
@@ -2001,9 +2033,6 @@
         try {
             if (userId != mCurrentUserId && !hasCrossUserPermission()) return;
 
-            final ComponentName component;
-            final int finalWhich;
-
             // Clear any previous ImageWallpaper related fields
             List<WallpaperData> toClear = new ArrayList<>();
             if ((which & FLAG_LOCK) > 0 && lockWallpaper != null) toClear.add(lockWallpaper);
@@ -2017,19 +2046,34 @@
                 }
             }
 
-            // lock only case: set the system wallpaper component to both screens
-            if (which == FLAG_LOCK) {
-                component = wallpaper.getComponent();
-                finalWhich = FLAG_LOCK | FLAG_SYSTEM;
+            final WallpaperDescription description;
+            final int finalWhich;
+
+            if (liveWallpaperContentHandling()) {
+                if (which == FLAG_LOCK) {
+                    // lock only case: set the system wallpaper component to both screens
+                    description = wallpaper.getDescription();
+                    finalWhich = FLAG_LOCK | FLAG_SYSTEM;
+                } else {
+                    description = new WallpaperDescription.Builder().build();
+                    finalWhich = which;
+                }
             } else {
-                component = null;
-                finalWhich = which;
+                if (which == FLAG_LOCK) {
+                    // lock only case: set the system wallpaper component to both screens
+                    description = new WallpaperDescription.Builder().setComponent(
+                            wallpaper.getComponent()).build();
+                    finalWhich = FLAG_LOCK | FLAG_SYSTEM;
+                } else {
+                    description = new WallpaperDescription.Builder().build();
+                    finalWhich = which;
+                }
             }
 
             // except for the lock case (for which we keep the system wallpaper as-is), force rebind
             boolean force = which != FLAG_LOCK;
-            boolean success = withCleanCallingIdentity(() -> setWallpaperComponentInternal(
-                    component, finalWhich, userId, force, fromForeground, reply));
+            boolean success = withCleanCallingIdentity(() -> setWallpaperDescriptionInternal(
+                    description, finalWhich, userId, force, fromForeground, reply));
             if (success) return;
         } catch (IllegalArgumentException e1) {
             e = e1;
@@ -2040,7 +2084,10 @@
         // let's not let it crash the system and just live with no
         // wallpaper.
         Slog.e(TAG, "Default wallpaper component not found!", e);
-        withCleanCallingIdentity(() -> clearWallpaperComponentLocked(wallpaper));
+        withCleanCallingIdentity(() -> {
+            wallpaper.mBindSource = BindSource.FALLBACK_DEFAULT_MISSING;
+            bindWallpaperComponentLocked(mImageWallpaper, true, false, wallpaper, reply);
+        });
         if (reply != null) {
             try {
                 reply.sendResult(null);
@@ -2412,6 +2459,9 @@
 
     @Override
     public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which, int userId) {
+        if (liveWallpaperContentHandling()) {
+            return getWallpaperInstance(which, userId, false).getInfo();
+        }
 
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                 Binder.getCallingUid(), userId, false, true, "getWallpaperInfo", null);
@@ -2425,13 +2475,45 @@
             WallpaperInfo info = wallpaper.connection.mInfo;
             if (hasPermission(READ_WALLPAPER_INTERNAL)
                     || mPackageManagerInternal.canQueryPackage(
-                            Binder.getCallingUid(), info.getComponent().getPackageName())) {
+                    Binder.getCallingUid(), info.getComponent().getPackageName())) {
                 return info;
             }
         }
         return null;
     }
 
+    @NonNull
+    @Override
+    public WallpaperInstance getWallpaperInstance(@SetWallpaperFlags int which, int userId) {
+        return getWallpaperInstance(which, userId, true);
+    }
+
+    private WallpaperInstance getWallpaperInstance(@SetWallpaperFlags int which, int userId,
+            boolean requireReadWallpaper) {
+        final WallpaperInstance defaultInstance = new WallpaperInstance(null,
+                new WallpaperDescription.Builder().build());
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, true, "getWallpaperInfo", null);
+        synchronized (mLock) {
+            WallpaperData wallpaper = (which == FLAG_LOCK) ? mLockWallpaperMap.get(userId)
+                    : mWallpaperMap.get(userId);
+            if (wallpaper == null
+                    || wallpaper.connection == null
+                    || wallpaper.connection.mInfo == null) {
+                return defaultInstance;
+            }
+
+            WallpaperInfo info = wallpaper.connection.mInfo;
+            boolean canQueryPackage = mPackageManagerInternal.canQueryPackage(
+                    Binder.getCallingUid(), info.getComponent().getPackageName());
+            if (hasPermission(READ_WALLPAPER_INTERNAL)
+                    || (canQueryPackage && !requireReadWallpaper)) {
+                return new WallpaperInstance(info, wallpaper.getDescription());
+            }
+        }
+        return defaultInstance;
+    }
+
     @Override
     public ParcelFileDescriptor getWallpaperInfoFile(int userId) {
         synchronized (mLock) {
@@ -3159,11 +3241,11 @@
     }
 
     @Override
-    public void setWallpaperComponentChecked(ComponentName name, String callingPackage,
-            @SetWallpaperFlags int which, int userId) {
+    public void setWallpaperComponentChecked(WallpaperDescription description,
+            String callingPackage, @SetWallpaperFlags int which, int userId) {
 
         if (isWallpaperSupported(callingPackage) && isSetWallpaperAllowed(callingPackage)) {
-            setWallpaperComponent(name, callingPackage, which, userId);
+            setWallpaperDescription(description, callingPackage, which, userId);
         }
     }
 
@@ -3176,12 +3258,23 @@
     @VisibleForTesting
     boolean setWallpaperComponent(ComponentName name, String callingPackage,
             @SetWallpaperFlags int which, int userId) {
-        boolean fromForeground = isFromForegroundApp(callingPackage);
-        return setWallpaperComponentInternal(name, which, userId, false, fromForeground, null);
+        return setWallpaperDescription(
+                new WallpaperDescription.Builder().setComponent(name).build(), callingPackage,
+                which, userId);
     }
 
-    private boolean setWallpaperComponentInternal(ComponentName name,  @SetWallpaperFlags int which,
-            int userIdIn, boolean force, boolean fromForeground, IRemoteCallback reply) {
+    @VisibleForTesting
+    boolean setWallpaperDescription(WallpaperDescription description, String callingPackage,
+            @SetWallpaperFlags int which, int userId) {
+        boolean fromForeground = isFromForegroundApp(callingPackage);
+        return setWallpaperDescriptionInternal(description, which, userId, false, fromForeground,
+                null);
+    }
+
+    private boolean setWallpaperDescriptionInternal(@NonNull WallpaperDescription description,
+            @SetWallpaperFlags int which, int userIdIn, boolean force, boolean fromForeground,
+            IRemoteCallback reply) {
+        ComponentName name = description.getComponent();
         if (DEBUG) {
             Slog.v(TAG, "Setting new live wallpaper: which=" + which + ", component: " + name);
         }
@@ -3191,11 +3284,11 @@
         checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
 
         boolean shouldNotifyColors = false;
+        final boolean bindSuccess;
 
         // If the lockscreen wallpaper is set to the same as the home screen, notify that the
         // lockscreen wallpaper colors changed, even if we don't bind a new wallpaper engine.
         boolean shouldNotifyLockscreenColors = false;
-        boolean bindSuccess;
         final WallpaperData newWallpaper;
 
         synchronized (mLock) {
@@ -3226,8 +3319,14 @@
                 final WallpaperDestinationChangeHandler
                         liveSync = new WallpaperDestinationChangeHandler(
                         newWallpaper);
-                boolean same = changingToSame(name, newWallpaper.connection,
-                        newWallpaper.getComponent());
+                boolean same;
+                if (liveWallpaperContentHandling()) {
+                    same = changingToSame(description, newWallpaper.connection,
+                            newWallpaper.getDescription());
+                } else {
+                    same = changingToSame(name, newWallpaper.connection,
+                            newWallpaper.getComponent());
+                }
 
                 /*
                  * If we have a shared system+lock wallpaper, and we reapply the same wallpaper
@@ -3238,8 +3337,13 @@
 
                 newWallpaper.mBindSource =
                         (name == null) ? BindSource.SET_LIVE_TO_CLEAR : BindSource.SET_LIVE;
-                bindSuccess = bindWallpaperComponentLocked(name, /* force */
-                        forceRebind, /* fromUser */ true, newWallpaper, reply);
+                if (liveWallpaperContentHandling()) {
+                    bindSuccess = bindWallpaperDescriptionLocked(description, forceRebind,
+                            /* fromUser */ true, newWallpaper, reply);
+                } else {
+                    bindSuccess = bindWallpaperComponentLocked(name, forceRebind,
+                            /* fromUser */ true, newWallpaper, reply);
+                }
                 if (bindSuccess) {
                     if (!same || (offloadColorExtraction() && forceRebind)) {
                         newWallpaper.primaryColors = null;
@@ -3335,6 +3439,24 @@
         return false;
     }
 
+    private boolean changingToSame(WallpaperDescription newDescription,
+            WallpaperConnection currentConnection, WallpaperDescription currentDescription) {
+        if (currentConnection == null) {
+            return false;
+        }
+        if (isDefaultComponent(newDescription.getComponent()) && isDefaultComponent(
+                currentDescription.getComponent())) {
+            if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
+            // Still using default wallpaper.
+            return true;
+        } else if (newDescription.equals(currentDescription)) {
+            // Changing to same wallpaper.
+            if (DEBUG) Slog.v(TAG, "same wallpaper");
+            return true;
+        }
+        return false;
+    }
+
     /*
      * Attempt to bind the wallpaper given by `componentName`, returning true on success otherwise
      * false.
@@ -3352,12 +3474,29 @@
      */
     boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
             boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
+        return bindWallpaperDescriptionLocked(
+                new WallpaperDescription.Builder().setComponent(componentName).build(), force,
+                fromUser, wallpaper, reply);
+    }
+
+    // When `liveWallpaperContentHandling()` is false this acts exactly like the version which takes
+    // a ComponentName argument did: it uses the ComponentName from `description`, it binds the
+    // service given by that component, and updates WallpaperData with that component on success.
+    boolean bindWallpaperDescriptionLocked(WallpaperDescription description, boolean force,
+            boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
+        ComponentName componentName = description.getComponent();
         if (DEBUG_LIVE) {
             Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
         }
-        // Has the component changed?
-        if (!force && changingToSame(componentName, wallpaper.connection,
-                wallpaper.getComponent())) {
+        boolean skipBinding;
+        if (liveWallpaperContentHandling()) {
+            skipBinding = !force && changingToSame(description, wallpaper.connection,
+                    wallpaper.getDescription());
+        } else {
+            skipBinding = !force && changingToSame(componentName, wallpaper.connection,
+                    wallpaper.getComponent());
+        }
+        if (skipBinding) {
             try {
                 if (DEBUG_LIVE) {
                     Slog.v(TAG, "Changing to the same component, ignoring");
@@ -3374,6 +3513,9 @@
         try {
             if (componentName == null) {
                 componentName = mDefaultWallpaperComponent;
+                if (liveWallpaperContentHandling()) {
+                    description = description.toBuilder().setComponent(componentName).build();
+                }
             }
             int serviceUserId = wallpaper.userId;
             ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
@@ -3494,7 +3636,11 @@
                 return false;
             }
             maybeDetachLastWallpapers(wallpaper);
-            wallpaper.setComponent(componentName);
+            if (liveWallpaperContentHandling()) {
+                wallpaper.setDescription(description);
+            } else {
+                wallpaper.setComponent(componentName);
+            }
             wallpaper.connection = newConn;
             newConn.mReply = reply;
             updateCurrentWallpapers(wallpaper);
@@ -3618,11 +3764,6 @@
                 });
     }
 
-    private void clearWallpaperComponentLocked(WallpaperData wallpaper) {
-        wallpaper.setComponent(null);
-        detachWallpaperLocked(wallpaper);
-    }
-
     private void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
         t.traceBegin("WPMS.attachServiceLocked");
@@ -3858,6 +3999,7 @@
     }
 
     // Called by SystemBackupAgent after files are restored to disk.
+    // TODO(b/373875373) Remove this method
     public void settingsRestored() {
         // Verify caller is the system
         if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
@@ -3876,6 +4018,14 @@
             ComponentName componentName =
                     removeNextWallpaperComponent() ? wallpaper.getComponent()
                             : wallpaper.nextWallpaperComponent;
+
+            if (liveWallpaperContentHandling()) {
+                // Per b/373875373 this method should be removed, so we just set wallpapers to
+                // default.
+                bindWallpaperDescriptionLocked(new WallpaperDescription.Builder().build(), false,
+                        false, wallpaper, null);
+                return;
+            }
             if (componentName != null && !componentName.equals(mImageWallpaper)) {
                 wallpaper.mBindSource = BindSource.RESTORE_SETTINGS_LIVE_SUCCESS;
                 if (!bindWallpaperComponentLocked(componentName, false, false,
@@ -3894,15 +4044,20 @@
                     if (DEBUG) Slog.v(TAG, "settingsRestored: name is empty");
                     success = true;
                 } else {
-                    if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
+                    if (DEBUG) {
+                        Slog.v(TAG, "settingsRestored: attempting to restore named resource");
+                    }
                     success = mWallpaperDataParser.restoreNamedResourceLocked(wallpaper);
                 }
-                if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
-                        + " id=" + wallpaper.wallpaperId);
+                if (DEBUG) {
+                    Slog.v(TAG, "settingsRestored: success=" + success + " id="
+                            + wallpaper.wallpaperId);
+                }
                 if (success) {
-                    mWallpaperCropper.generateCrop(wallpaper); // based on the new image + metadata
+                    mWallpaperCropper.generateCrop(
+                            wallpaper); // based on the new image + metadata
                     wallpaper.mBindSource = BindSource.RESTORE_SETTINGS_STATIC;
-                    bindWallpaperComponentLocked(componentName, true, false, wallpaper, null);
+                    bindWallpaperComponentLocked(null, true, false, wallpaper, null);
                 }
             }
         }
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/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index ab5316f..92ce251 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -16,8 +16,6 @@
 
 package com.android.server.webkit;
 
-import static android.webkit.Flags.updateServiceV2;
-
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -253,46 +251,11 @@
     }
 
     @Override
-    public int getMultiProcessSetting() {
-        if (updateServiceV2()) {
-            throw new IllegalStateException(
-                    "getMultiProcessSetting shouldn't be called if update_service_v2 flag is set.");
-        }
-        return Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.WEBVIEW_MULTIPROCESS, 0);
-    }
-
-    @Override
-    public void setMultiProcessSetting(int value) {
-        if (updateServiceV2()) {
-            throw new IllegalStateException(
-                    "setMultiProcessSetting shouldn't be called if update_service_v2 flag is set.");
-        }
-        Settings.Global.putInt(
-                mContext.getContentResolver(), Settings.Global.WEBVIEW_MULTIPROCESS, value);
-    }
-
-    @Override
-    public void notifyZygote(boolean enableMultiProcess) {
-        if (updateServiceV2()) {
-            throw new IllegalStateException(
-                    "notifyZygote shouldn't be called if update_service_v2 flag is set.");
-        }
-        WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
-    }
-
-    @Override
     public void ensureZygoteStarted() {
         WebViewZygote.getProcess();
     }
 
     @Override
-    public boolean isMultiProcessDefaultEnabled() {
-        // Multiprocess is enabled by default for all devices.
-        return true;
-    }
-
-    @Override
     public void pinWebviewIfRequired(ApplicationInfo appInfo) {
         PinnerService pinnerService = LocalServices.getService(PinnerService.class);
         int webviewPinQuota = pinnerService.getWebviewPinQuota();
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 3b77d07..6710554 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -55,12 +55,8 @@
      */
     List<UserPackage> getPackageInfoForProviderAllUsers(WebViewProviderInfo configInfo);
 
-    int getMultiProcessSetting();
-    void setMultiProcessSetting(int value);
-    void notifyZygote(boolean enableMultiProcess);
     /** Start the zygote if it's not already running. */
     void ensureZygoteStarted();
-    boolean isMultiProcessDefaultEnabled();
 
     void pinWebviewIfRequired(ApplicationInfo appInfo);
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 7acb864..e1fa884 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -16,8 +16,6 @@
 
 package com.android.server.webkit;
 
-import static android.webkit.Flags.updateServiceV2;
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -63,7 +61,7 @@
             new Histogram.ScaledRangeOptions(20, 0, 1, 1.4f));
 
     private BroadcastReceiver mWebViewUpdatedReceiver;
-    private WebViewUpdateServiceInterface mImpl;
+    private WebViewUpdateServiceImpl2 mImpl;
 
     static final int PACKAGE_CHANGED = 0;
     static final int PACKAGE_ADDED = 1;
@@ -72,11 +70,7 @@
 
     public WebViewUpdateService(Context context) {
         super(context);
-        if (updateServiceV2()) {
-            mImpl = new WebViewUpdateServiceImpl2(new SystemImpl(context));
-        } else {
-            mImpl = new WebViewUpdateServiceImpl(new SystemImpl(context));
-        }
+        mImpl = new WebViewUpdateServiceImpl2(new SystemImpl(context));
     }
 
     @Override
@@ -84,8 +78,13 @@
         mWebViewUpdatedReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
+                    final String action = intent.getAction();
+                    if (action == null) {
+                        return;
+                    }
+
                     int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                    switch (intent.getAction()) {
+                    switch (action) {
                         case Intent.ACTION_PACKAGE_REMOVED:
                             // When a package is replaced we will receive two intents, one
                             // representing the removal of the old package and one representing the
@@ -170,13 +169,8 @@
         public void onShellCommand(FileDescriptor in, FileDescriptor out,
                 FileDescriptor err, String[] args, ShellCallback callback,
                 ResultReceiver resultReceiver) {
-            if (updateServiceV2()) {
-                (new WebViewUpdateServiceShellCommand2(this))
-                        .exec(this, in, out, err, args, callback, resultReceiver);
-            } else {
-                (new WebViewUpdateServiceShellCommand(this))
-                        .exec(this, in, out, err, args, callback, resultReceiver);
-            }
+            (new WebViewUpdateServiceShellCommand2(this))
+                    .exec(this, in, out, err, args, callback, resultReceiver);
         }
 
 
@@ -300,45 +294,6 @@
             return currentWebViewPackage;
         }
 
-        @Override // Binder call
-        public boolean isMultiProcessEnabled() {
-            if (updateServiceV2()) {
-                throw new IllegalStateException(
-                        "isMultiProcessEnabled shouldn't be called if update_service_v2 flag is"
-                                + " set.");
-            }
-            return WebViewUpdateService.this.mImpl.isMultiProcessEnabled();
-        }
-
-        @Override // Binder call
-        public void enableMultiProcess(boolean enable) {
-            if (updateServiceV2()) {
-                throw new IllegalStateException(
-                        "enableMultiProcess shouldn't be called if update_service_v2 flag is set.");
-            }
-            if (getContext()
-                            .checkCallingPermission(
-                                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
-                    != PackageManager.PERMISSION_GRANTED) {
-                String msg =
-                        "Permission Denial: enableMultiProcess() from pid="
-                                + Binder.getCallingPid()
-                                + ", uid="
-                                + Binder.getCallingUid()
-                                + " requires "
-                                + android.Manifest.permission.WRITE_SECURE_SETTINGS;
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
-
-            final long callingId = Binder.clearCallingIdentity();
-            try {
-                WebViewUpdateService.this.mImpl.enableMultiProcess(enable);
-            } finally {
-                Binder.restoreCallingIdentity(callingId);
-            }
-        }
-
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
deleted file mode 100644
index b9be4a2..0000000
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ /dev/null
@@ -1,768 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.webkit;
-
-import android.annotation.Nullable;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.Signature;
-import android.os.AsyncTask;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.util.AndroidRuntimeException;
-import android.util.Slog;
-import android.webkit.UserPackage;
-import android.webkit.WebViewFactory;
-import android.webkit.WebViewProviderInfo;
-import android.webkit.WebViewProviderResponse;
-
-import com.android.modules.expresslog.Counter;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Implementation of the WebViewUpdateService.
- * This class doesn't depend on the android system like the actual Service does and can be used
- * directly by tests (as long as they implement a SystemInterface).
- *
- * This class keeps track of and prepares the current WebView implementation, and needs to keep
- * track of a couple of different things such as what package is used as WebView implementation.
- *
- * The package-visible methods in this class are accessed from WebViewUpdateService either on the UI
- * thread or on one of multiple Binder threads. The WebView preparation code shares state between
- * threads meaning that code that chooses a new WebView implementation or checks which
- * implementation is being used needs to hold a lock.
- *
- * The WebViewUpdateService can be accessed in a couple of different ways.
- * 1. It is started from the SystemServer at boot - at that point we just initiate some state such
- * as the WebView preparation class.
- * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot
- * and the WebViewUpdateService should not have been accessed before this call. In this call we
- * choose WebView implementation for the first time.
- * 3. The update service listens for Intents related to package installs and removals. These intents
- * are received and processed on the UI thread. Each intent can result in changing WebView
- * implementation.
- * 4. The update service can be reached through Binder calls which are handled on specific binder
- * threads. These calls can be made from any process. Generally they are used for changing WebView
- * implementation (from Settings), getting information about the current WebView implementation (for
- * loading WebView into an app process), or notifying the service about Relro creation being
- * completed.
- *
- * @hide
- */
-class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
-    private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName();
-
-    private static class WebViewPackageMissingException extends Exception {
-        WebViewPackageMissingException(String message) {
-            super(message);
-        }
-
-        WebViewPackageMissingException(Exception e) {
-            super(e);
-        }
-    }
-
-    private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000.
-    private static final long NS_PER_MS = 1000000;
-
-    private static final int VALIDITY_OK = 0;
-    private static final int VALIDITY_INCORRECT_SDK_VERSION = 1;
-    private static final int VALIDITY_INCORRECT_VERSION_CODE = 2;
-    private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
-    private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
-
-    private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
-    private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
-
-    private final SystemInterface mSystemInterface;
-
-    private long mMinimumVersionCode = -1;
-
-    // Keeps track of the number of running relro creations
-    private int mNumRelroCreationsStarted = 0;
-    private int mNumRelroCreationsFinished = 0;
-    // Implies that we need to rerun relro creation because we are using an out-of-date package
-    private boolean mWebViewPackageDirty = false;
-    private boolean mAnyWebViewInstalled = false;
-
-    private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
-
-    // The WebView package currently in use (or the one we are preparing).
-    private PackageInfo mCurrentWebViewPackage = null;
-
-    private final Object mLock = new Object();
-
-    WebViewUpdateServiceImpl(SystemInterface systemInterface) {
-        mSystemInterface = systemInterface;
-    }
-
-    @Override
-    public void packageStateChanged(String packageName, int changedState, int userId) {
-        // We don't early out here in different cases where we could potentially early-out (e.g. if
-        // we receive PACKAGE_CHANGED for another user than the system user) since that would
-        // complicate this logic further and open up for more edge cases.
-        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
-            String webviewPackage = provider.packageName;
-
-            if (webviewPackage.equals(packageName)) {
-                boolean updateWebView = false;
-                boolean removedOrChangedOldPackage = false;
-                String oldProviderName = null;
-                PackageInfo newPackage = null;
-                synchronized (mLock) {
-                    try {
-                        newPackage = findPreferredWebViewPackage();
-                        if (mCurrentWebViewPackage != null) {
-                            oldProviderName = mCurrentWebViewPackage.packageName;
-                        }
-                        // Only trigger update actions if the updated package is the one
-                        // that will be used, or the one that was in use before the
-                        // update, or if we haven't seen a valid WebView package before.
-                        updateWebView =
-                            provider.packageName.equals(newPackage.packageName)
-                            || provider.packageName.equals(oldProviderName)
-                            || mCurrentWebViewPackage == null;
-                        // We removed the old package if we received an intent to remove
-                        // or replace the old package.
-                        removedOrChangedOldPackage =
-                            provider.packageName.equals(oldProviderName);
-                        if (updateWebView) {
-                            onWebViewProviderChanged(newPackage);
-                        }
-                    } catch (WebViewPackageMissingException e) {
-                        mCurrentWebViewPackage = null;
-                        Slog.e(TAG, "Could not find valid WebView package to create relro with "
-                                + e);
-                    }
-                }
-                if (updateWebView && !removedOrChangedOldPackage
-                        && oldProviderName != null) {
-                    // If the provider change is the result of adding or replacing a
-                    // package that was not the previous provider then we must kill
-                    // packages dependent on the old package ourselves. The framework
-                    // only kills dependents of packages that are being removed.
-                    mSystemInterface.killPackageDependents(oldProviderName);
-                }
-                return;
-            }
-        }
-    }
-
-    @Override
-    public void prepareWebViewInSystemServer() {
-        mSystemInterface.notifyZygote(isMultiProcessEnabled());
-        try {
-            synchronized (mLock) {
-                mCurrentWebViewPackage = findPreferredWebViewPackage();
-                String userSetting = mSystemInterface.getUserChosenWebViewProvider();
-                if (userSetting != null
-                        && !userSetting.equals(mCurrentWebViewPackage.packageName)) {
-                    // Don't persist the user-chosen setting across boots if the package being
-                    // chosen is not used (could be disabled or uninstalled) so that the user won't
-                    // be surprised by the device switching to using a certain webview package,
-                    // that was uninstalled/disabled a long time ago, if it is installed/enabled
-                    // again.
-                    mSystemInterface.updateUserSetting(mCurrentWebViewPackage.packageName);
-                }
-                onWebViewProviderChanged(mCurrentWebViewPackage);
-            }
-        } catch (WebViewPackageMissingException e) {
-            Slog.e(TAG, "Could not find valid WebView package to create relro with", e);
-        } catch (Throwable t) {
-            // We don't know a case when this should happen but we log and discard errors at this
-            // stage as we must not crash the system server.
-            Slog.wtf(TAG, "error preparing webview provider from system server", t);
-        }
-
-        if (getCurrentWebViewPackage() == null) {
-            // We didn't find a valid WebView implementation. Try explicitly re-enabling the
-            // fallback package for all users in case it was disabled, even if we already did the
-            // one-time migration before. If this actually changes the state, we will see the
-            // PackageManager broadcast shortly and try again.
-            WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
-            WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
-            if (fallbackProvider != null) {
-                Slog.w(TAG, "No valid provider, trying to enable " + fallbackProvider.packageName);
-                mSystemInterface.enablePackageForAllUsers(fallbackProvider.packageName, true);
-            } else {
-                Slog.e(TAG, "No valid provider and no fallback available.");
-            }
-        }
-    }
-
-    private void startZygoteWhenReady() {
-        // Wait on a background thread for RELRO creation to be done. We ignore the return value
-        // because even if RELRO creation failed we still want to start the zygote.
-        waitForAndGetProvider();
-        mSystemInterface.ensureZygoteStarted();
-    }
-
-    @Override
-    public void handleNewUser(int userId) {
-        // The system user is always started at boot, and by that point we have already run one
-        // round of the package-changing logic (through prepareWebViewInSystemServer()), so early
-        // out here.
-        if (userId == UserHandle.USER_SYSTEM) return;
-        handleUserChange();
-    }
-
-    @Override
-    public void handleUserRemoved(int userId) {
-        handleUserChange();
-    }
-
-    /**
-     * Called when a user was added or removed to ensure WebView preparation is triggered.
-     * This has to be done since the WebView package we use depends on the enabled-state
-     * of packages for all users (so adding or removing a user might cause us to change package).
-     */
-    private void handleUserChange() {
-        // Potentially trigger package-changing logic.
-        updateCurrentWebViewPackage(null);
-    }
-
-    @Override
-    public void notifyRelroCreationCompleted() {
-        synchronized (mLock) {
-            mNumRelroCreationsFinished++;
-            checkIfRelrosDoneLocked();
-        }
-    }
-
-    @Override
-    public WebViewProviderResponse waitForAndGetProvider() {
-        PackageInfo webViewPackage = null;
-        final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
-        boolean webViewReady = false;
-        int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
-        synchronized (mLock) {
-            webViewReady = webViewIsReadyLocked();
-            while (!webViewReady) {
-                final long timeNowMs = System.nanoTime() / NS_PER_MS;
-                if (timeNowMs >= timeoutTimeMs) break;
-                try {
-                    mLock.wait(timeoutTimeMs - timeNowMs);
-                } catch (InterruptedException e) {
-                    // ignore
-                }
-                webViewReady = webViewIsReadyLocked();
-            }
-            // Make sure we return the provider that was used to create the relro file
-            webViewPackage = mCurrentWebViewPackage;
-            if (webViewReady) {
-                // success
-            } else if (!mAnyWebViewInstalled) {
-                webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
-            } else {
-                // Either the current relro creation  isn't done yet, or the new relro creatioin
-                // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
-                webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
-                String timeoutError = "Timed out waiting for relro creation, relros started "
-                        + mNumRelroCreationsStarted
-                        + " relros finished " + mNumRelroCreationsFinished
-                        + " package dirty? " + mWebViewPackageDirty;
-                Slog.e(TAG, timeoutError);
-                Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, timeoutError);
-            }
-        }
-        if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
-        return new WebViewProviderResponse(webViewPackage, webViewStatus);
-    }
-
-    /**
-     * Change WebView provider and provider setting and kill packages using the old provider.
-     * Return the new provider (in case we are in the middle of creating relro files, or
-     * replacing that provider it will not be in use directly, but will be used when the relros
-     * or the replacement are done).
-     */
-    @Override
-    public String changeProviderAndSetting(String newProviderName) {
-        PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
-        if (newPackage == null) return "";
-        return newPackage.packageName;
-    }
-
-    /**
-     * Update the current WebView package.
-     * @param newProviderName the package to switch to, null if no package has been explicitly
-     * chosen.
-     */
-    private PackageInfo updateCurrentWebViewPackage(@Nullable String newProviderName) {
-        PackageInfo oldPackage = null;
-        PackageInfo newPackage = null;
-        boolean providerChanged = false;
-        synchronized (mLock) {
-            oldPackage = mCurrentWebViewPackage;
-
-            if (newProviderName != null) {
-                mSystemInterface.updateUserSetting(newProviderName);
-            }
-
-            try {
-                newPackage = findPreferredWebViewPackage();
-                providerChanged = (oldPackage == null)
-                        || !newPackage.packageName.equals(oldPackage.packageName);
-            } catch (WebViewPackageMissingException e) {
-                // If updated the Setting but don't have an installed WebView package, the
-                // Setting will be used when a package is available.
-                mCurrentWebViewPackage = null;
-                Slog.e(TAG, "Couldn't find WebView package to use " + e);
-                return null;
-            }
-            // Perform the provider change if we chose a new provider
-            if (providerChanged) {
-                onWebViewProviderChanged(newPackage);
-            }
-        }
-        // Kill apps using the old provider only if we changed provider
-        if (providerChanged && oldPackage != null) {
-            mSystemInterface.killPackageDependents(oldPackage.packageName);
-        }
-        // Return the new provider, this is not necessarily the one we were asked to switch to,
-        // but the persistent setting will now be pointing to the provider we were asked to
-        // switch to anyway.
-        return newPackage;
-    }
-
-    /**
-     * This is called when we change WebView provider, either when the current provider is
-     * updated or a new provider is chosen / takes precedence.
-     */
-    private void onWebViewProviderChanged(PackageInfo newPackage) {
-        synchronized (mLock) {
-            mAnyWebViewInstalled = true;
-            if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
-                mSystemInterface.pinWebviewIfRequired(newPackage.applicationInfo);
-                mCurrentWebViewPackage = newPackage;
-
-                // The relro creations might 'finish' (not start at all) before
-                // WebViewFactory.onWebViewProviderChanged which means we might not know the
-                // number of started creations before they finish.
-                mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
-                mNumRelroCreationsFinished = 0;
-                mNumRelroCreationsStarted =
-                    mSystemInterface.onWebViewProviderChanged(newPackage);
-                Counter.logIncrement("webview.value_on_webview_provider_changed_counter");
-                if (newPackage.packageName.equals(getDefaultWebViewPackage().packageName)) {
-                    Counter.logIncrement(
-                            "webview.value_on_webview_provider_changed_"
-                            + "with_default_package_counter");
-                }
-                // If the relro creations finish before we know the number of started creations
-                // we will have to do any cleanup/notifying here.
-                checkIfRelrosDoneLocked();
-            } else {
-                mWebViewPackageDirty = true;
-            }
-        }
-
-        // Once we've notified the system that the provider has changed and started RELRO creation,
-        // try to restart the zygote so that it will be ready when apps use it.
-        if (isMultiProcessEnabled()) {
-            AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady);
-        }
-    }
-
-    /**
-     * Fetch only the currently valid WebView packages.
-     **/
-    @Override
-    public WebViewProviderInfo[] getValidWebViewPackages() {
-        ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
-        WebViewProviderInfo[] providers =
-            new WebViewProviderInfo[providersAndPackageInfos.length];
-        for (int n = 0; n < providersAndPackageInfos.length; n++) {
-            providers[n] = providersAndPackageInfos[n].provider;
-        }
-        return providers;
-    }
-
-    @Override
-    public WebViewProviderInfo getDefaultWebViewPackage() {
-        for (WebViewProviderInfo provider : getWebViewPackages()) {
-            if (provider.availableByDefault) {
-                return provider;
-            }
-        }
-
-        // This should be unreachable because the config parser enforces that there is at least
-        // one availableByDefault provider.
-        throw new AndroidRuntimeException("No available by default WebView Provider.");
-    }
-
-    private static class ProviderAndPackageInfo {
-        public final WebViewProviderInfo provider;
-        public final PackageInfo packageInfo;
-
-        ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
-            this.provider = provider;
-            this.packageInfo = packageInfo;
-        }
-    }
-
-    private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
-        WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
-        List<ProviderAndPackageInfo> providers = new ArrayList<>();
-        for (int n = 0; n < allProviders.length; n++) {
-            try {
-                PackageInfo packageInfo =
-                        mSystemInterface.getPackageInfoForProvider(allProviders[n]);
-                if (validityResult(allProviders[n], packageInfo) == VALIDITY_OK) {
-                    providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
-                }
-            } catch (NameNotFoundException e) {
-                // Don't add non-existent packages
-            }
-        }
-        return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
-    }
-
-    /**
-     * Returns either the package info of the WebView provider determined in the following way:
-     * If the user has chosen a provider then use that if it is valid,
-     * otherwise use the first package in the webview priority list that is valid.
-     *
-     */
-    private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException {
-        ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
-
-        String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider();
-
-        // If the user has chosen provider, use that (if it's installed and enabled for all
-        // users).
-        for (ProviderAndPackageInfo providerAndPackage : providers) {
-            if (providerAndPackage.provider.packageName.equals(userChosenProvider)) {
-                // userPackages can contain null objects.
-                List<UserPackage> userPackages =
-                        mSystemInterface.getPackageInfoForProviderAllUsers(
-                                providerAndPackage.provider);
-                if (isInstalledAndEnabledForAllUsers(userPackages)) {
-                    return providerAndPackage.packageInfo;
-                }
-            }
-        }
-
-        // User did not choose, or the choice failed; use the most stable provider that is
-        // installed and enabled for all users, and available by default (not through
-        // user choice).
-        for (ProviderAndPackageInfo providerAndPackage : providers) {
-            if (providerAndPackage.provider.availableByDefault) {
-                // userPackages can contain null objects.
-                List<UserPackage> userPackages =
-                        mSystemInterface.getPackageInfoForProviderAllUsers(
-                                providerAndPackage.provider);
-                if (isInstalledAndEnabledForAllUsers(userPackages)) {
-                    return providerAndPackage.packageInfo;
-                }
-            }
-        }
-
-        // This should never happen during normal operation (only with modified system images).
-        mAnyWebViewInstalled = false;
-        throw new WebViewPackageMissingException("Could not find a loadable WebView package");
-    }
-
-    /**
-     * Return true iff {@param packageInfos} point to only installed and enabled packages.
-     * The given packages {@param packageInfos} should all be pointing to the same package, but each
-     * PackageInfo representing a different user's package.
-     */
-    private static boolean isInstalledAndEnabledForAllUsers(
-            List<UserPackage> userPackages) {
-        for (UserPackage userPackage : userPackages) {
-            if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public WebViewProviderInfo[] getWebViewPackages() {
-        return mSystemInterface.getWebViewPackages();
-    }
-
-    @Override
-    public PackageInfo getCurrentWebViewPackage() {
-        synchronized (mLock) {
-            return mCurrentWebViewPackage;
-        }
-    }
-
-    /**
-     * Returns whether WebView is ready and is not going to go through its preparation phase
-     * again directly.
-     */
-    private boolean webViewIsReadyLocked() {
-        return !mWebViewPackageDirty
-            && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
-            // The current package might be replaced though we haven't received an intent
-            // declaring this yet, the following flag makes anyone loading WebView to wait in
-            // this case.
-            && mAnyWebViewInstalled;
-    }
-
-    private void checkIfRelrosDoneLocked() {
-        if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
-            if (mWebViewPackageDirty) {
-                mWebViewPackageDirty = false;
-                // If we have changed provider since we started the relro creation we need to
-                // redo the whole process using the new package instead.
-                try {
-                    PackageInfo newPackage = findPreferredWebViewPackage();
-                    onWebViewProviderChanged(newPackage);
-                } catch (WebViewPackageMissingException e) {
-                    mCurrentWebViewPackage = null;
-                    // If we can't find any valid WebView package we are now in a state where
-                    // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
-                    // should simply wait until we receive an intent declaring a new package was
-                    // installed.
-                }
-            } else {
-                mLock.notifyAll();
-            }
-        }
-    }
-
-    private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
-        // Ensure the provider targets this framework release (or a later one).
-        if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
-            return VALIDITY_INCORRECT_SDK_VERSION;
-        }
-        if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
-                && !mSystemInterface.systemIsDebuggable()) {
-            // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
-            // minimum version code. This check is only enforced for user builds.
-            return VALIDITY_INCORRECT_VERSION_CODE;
-        }
-        if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) {
-            return VALIDITY_INCORRECT_SIGNATURE;
-        }
-        if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) {
-            return VALIDITY_NO_LIBRARY_FLAG;
-        }
-        return VALIDITY_OK;
-    }
-
-    /**
-     * Both versionCodes should be from a WebView provider package implemented by Chromium.
-     * VersionCodes from other kinds of packages won't make any sense in this method.
-     *
-     * An introduction to Chromium versionCode scheme:
-     * "BBBBPPPXX"
-     * BBBB: 4 digit branch number. It monotonically increases over time.
-     * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits
-     * may change their meaning in the future.
-     * XX: Digits to differentiate different APK builds of the same source version.
-     *
-     * This method takes the "BBBB" of versionCodes and compare them.
-     *
-     * https://www.chromium.org/developers/version-numbers describes general Chromium versioning;
-     * https://source.chromium.org/chromium/chromium/src/+/master:build/util/android_chrome_version.py
-     * is the canonical source for how Chromium versionCodes are calculated.
-     *
-     * @return true if versionCode1 is higher than or equal to versionCode2.
-     */
-    private static boolean versionCodeGE(long versionCode1, long versionCode2) {
-        long v1 = versionCode1 / 100000;
-        long v2 = versionCode2 / 100000;
-
-        return v1 >= v2;
-    }
-
-    /**
-     * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
-     * of all available-by-default WebView provider packages. If there is no such WebView provider
-     * package on the system, then return -1, which means all positive versionCode WebView packages
-     * are accepted.
-     *
-     * Note that this is a private method that handles a variable (mMinimumVersionCode) which is
-     * shared between threads. Furthermore, this method does not hold mLock meaning that we must
-     * take extra care to ensure this method is thread-safe.
-     */
-    private long getMinimumVersionCode() {
-        if (mMinimumVersionCode > 0) {
-            return mMinimumVersionCode;
-        }
-
-        long minimumVersionCode = -1;
-        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
-            if (provider.availableByDefault) {
-                try {
-                    long versionCode =
-                            mSystemInterface.getFactoryPackageVersion(provider.packageName);
-                    if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
-                        minimumVersionCode = versionCode;
-                    }
-                } catch (NameNotFoundException e) {
-                    // Safe to ignore.
-                }
-            }
-        }
-
-        mMinimumVersionCode = minimumVersionCode;
-        return mMinimumVersionCode;
-    }
-
-    private static boolean providerHasValidSignature(WebViewProviderInfo provider,
-            PackageInfo packageInfo, SystemInterface systemInterface) {
-        // Skip checking signatures on debuggable builds, for development purposes.
-        if (systemInterface.systemIsDebuggable()) return true;
-
-        // Allow system apps to be valid providers regardless of signature.
-        if (packageInfo.applicationInfo.isSystemApp()) return true;
-
-        // We don't support packages with multiple signatures.
-        if (packageInfo.signatures.length != 1) return false;
-
-        // If any of the declared signatures match the package signature, it's valid.
-        for (Signature signature : provider.signatures) {
-            if (signature.equals(packageInfo.signatures[0])) return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Returns the only fallback provider in the set of given packages, or null if there is none.
-     */
-    private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
-        for (WebViewProviderInfo provider : webviewPackages) {
-            if (provider.isFallback) {
-                return provider;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public boolean isMultiProcessEnabled() {
-        int settingValue = mSystemInterface.getMultiProcessSetting();
-        if (mSystemInterface.isMultiProcessDefaultEnabled()) {
-            // Multiprocess should be enabled unless the user has turned it off manually.
-            return settingValue > MULTIPROCESS_SETTING_OFF_VALUE;
-        } else {
-            // Multiprocess should not be enabled, unless the user has turned it on manually.
-            return settingValue >= MULTIPROCESS_SETTING_ON_VALUE;
-        }
-    }
-
-    @Override
-    public void enableMultiProcess(boolean enable) {
-        PackageInfo current = getCurrentWebViewPackage();
-        mSystemInterface.setMultiProcessSetting(
-                enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE);
-        mSystemInterface.notifyZygote(enable);
-        if (current != null) {
-            mSystemInterface.killPackageDependents(current.packageName);
-        }
-    }
-
-    /**
-     * Dump the state of this Service.
-     */
-    @Override
-    public void dumpState(PrintWriter pw) {
-        pw.println("Current WebView Update Service state");
-        pw.println(String.format("  Multiprocess enabled: %b", isMultiProcessEnabled()));
-        synchronized (mLock) {
-            if (mCurrentWebViewPackage == null) {
-                pw.println("  Current WebView package is null");
-            } else {
-                pw.println(String.format("  Current WebView package (name, version): (%s, %s)",
-                        mCurrentWebViewPackage.packageName,
-                        mCurrentWebViewPackage.versionName));
-            }
-            pw.println(String.format("  Minimum targetSdkVersion: %d",
-                    UserPackage.MINIMUM_SUPPORTED_SDK));
-            pw.println(String.format("  Minimum WebView version code: %d",
-                    mMinimumVersionCode));
-            pw.println(String.format("  Number of relros started: %d",
-                    mNumRelroCreationsStarted));
-            pw.println(String.format("  Number of relros finished: %d",
-                        mNumRelroCreationsFinished));
-            pw.println(String.format("  WebView package dirty: %b", mWebViewPackageDirty));
-            pw.println(String.format("  Any WebView package installed: %b",
-                    mAnyWebViewInstalled));
-
-            try {
-                PackageInfo preferredWebViewPackage = findPreferredWebViewPackage();
-                pw.println(String.format(
-                        "  Preferred WebView package (name, version): (%s, %s)",
-                        preferredWebViewPackage.packageName,
-                        preferredWebViewPackage.versionName));
-            } catch (WebViewPackageMissingException e) {
-                pw.println(String.format("  Preferred WebView package: none"));
-            }
-
-            dumpAllPackageInformationLocked(pw);
-        }
-    }
-
-    private void dumpAllPackageInformationLocked(PrintWriter pw) {
-        WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
-        pw.println("  WebView packages:");
-        for (WebViewProviderInfo provider : allProviders) {
-            List<UserPackage> userPackages =
-                    mSystemInterface.getPackageInfoForProviderAllUsers(provider);
-            PackageInfo systemUserPackageInfo =
-                    userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo();
-            if (systemUserPackageInfo == null) {
-                pw.println(String.format("    %s is NOT installed.", provider.packageName));
-                continue;
-            }
-
-            int validity = validityResult(provider, systemUserPackageInfo);
-            String packageDetails = String.format(
-                    "versionName: %s, versionCode: %d, targetSdkVersion: %d",
-                    systemUserPackageInfo.versionName,
-                    systemUserPackageInfo.getLongVersionCode(),
-                    systemUserPackageInfo.applicationInfo.targetSdkVersion);
-            if (validity == VALIDITY_OK) {
-                boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
-                        mSystemInterface.getPackageInfoForProviderAllUsers(provider));
-                pw.println(String.format(
-                        "    Valid package %s (%s) is %s installed/enabled for all users",
-                        systemUserPackageInfo.packageName,
-                        packageDetails,
-                        installedForAllUsers ? "" : "NOT"));
-            } else {
-                pw.println(String.format("    Invalid package %s (%s), reason: %s",
-                        systemUserPackageInfo.packageName,
-                        packageDetails,
-                        getInvalidityReason(validity)));
-            }
-        }
-    }
-
-    private static String getInvalidityReason(int invalidityReason) {
-        switch (invalidityReason) {
-            case VALIDITY_INCORRECT_SDK_VERSION:
-                return "SDK version too low";
-            case VALIDITY_INCORRECT_VERSION_CODE:
-                return "Version code too low";
-            case VALIDITY_INCORRECT_SIGNATURE:
-                return "Incorrect signature";
-            case VALIDITY_NO_LIBRARY_FLAG:
-                return "No WebView-library manifest flag";
-            default:
-                return "Unexcepted validity-reason";
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index 307c15b..a5a02cd 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -66,7 +66,7 @@
  *
  * @hide
  */
-class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
+class WebViewUpdateServiceImpl2 {
     private static final String TAG = WebViewUpdateServiceImpl2.class.getSimpleName();
 
     private static class WebViewPackageMissingException extends Exception {
@@ -125,7 +125,6 @@
         mDefaultProvider = defaultProvider;
     }
 
-    @Override
     public void packageStateChanged(String packageName, int changedState, int userId) {
         // We don't early out here in different cases where we could potentially early-out (e.g. if
         // we receive PACKAGE_CHANGED for another user than the system user) since that would
@@ -216,7 +215,6 @@
         mSystemInterface.enablePackageForAllUsers(mDefaultProvider.packageName, true);
     }
 
-    @Override
     public void prepareWebViewInSystemServer() {
         try {
             boolean repairNeeded = true;
@@ -256,7 +254,6 @@
         mSystemInterface.ensureZygoteStarted();
     }
 
-    @Override
     public void handleNewUser(int userId) {
         // The system user is always started at boot, and by that point we have already run one
         // round of the package-changing logic (through prepareWebViewInSystemServer()), so early
@@ -265,7 +262,6 @@
         handleUserChange();
     }
 
-    @Override
     public void handleUserRemoved(int userId) {
         handleUserChange();
     }
@@ -280,7 +276,6 @@
         updateCurrentWebViewPackage(null);
     }
 
-    @Override
     public void notifyRelroCreationCompleted() {
         synchronized (mLock) {
             mNumRelroCreationsFinished++;
@@ -288,7 +283,6 @@
         }
     }
 
-    @Override
     public WebViewProviderResponse waitForAndGetProvider() {
         PackageInfo webViewPackage = null;
         final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
@@ -334,7 +328,6 @@
      * replacing that provider it will not be in use directly, but will be used when the relros
      * or the replacement are done).
      */
-    @Override
     public String changeProviderAndSetting(String newProviderName) {
         PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName);
         if (newPackage == null) return "";
@@ -430,7 +423,6 @@
     }
 
     /** Fetch only the currently valid WebView packages. */
-    @Override
     public WebViewProviderInfo[] getValidWebViewPackages() {
         ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
         WebViewProviderInfo[] providers =
@@ -445,7 +437,6 @@
      * Returns the default WebView provider which should be first availableByDefault option in the
      * system config.
      */
-    @Override
     public WebViewProviderInfo getDefaultWebViewPackage() {
         return mDefaultProvider;
     }
@@ -550,12 +541,10 @@
         return true;
     }
 
-    @Override
     public WebViewProviderInfo[] getWebViewPackages() {
         return mSystemInterface.getWebViewPackages();
     }
 
-    @Override
     public PackageInfo getCurrentWebViewPackage() {
         synchronized (mLock) {
             return mCurrentWebViewPackage;
@@ -708,20 +697,7 @@
         return null;
     }
 
-    @Override
-    public boolean isMultiProcessEnabled() {
-        throw new IllegalStateException(
-                "isMultiProcessEnabled shouldn't be called if update_service_v2 flag is set.");
-    }
-
-    @Override
-    public void enableMultiProcess(boolean enable) {
-        throw new IllegalStateException(
-                "enableMultiProcess shouldn't be called if update_service_v2 flag is set.");
-    }
-
     /** Dump the state of this Service. */
-    @Override
     public void dumpState(PrintWriter pw) {
         pw.println("Current WebView Update Service state");
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
deleted file mode 100644
index 1772ef9..0000000
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.webkit;
-
-import android.content.pm.PackageInfo;
-import android.webkit.WebViewProviderInfo;
-import android.webkit.WebViewProviderResponse;
-
-import java.io.PrintWriter;
-
-interface WebViewUpdateServiceInterface {
-    void packageStateChanged(String packageName, int changedState, int userId);
-
-    void handleNewUser(int userId);
-
-    void handleUserRemoved(int userId);
-
-    WebViewProviderInfo[] getWebViewPackages();
-
-    void prepareWebViewInSystemServer();
-
-    void notifyRelroCreationCompleted();
-
-    WebViewProviderResponse waitForAndGetProvider();
-
-    String changeProviderAndSetting(String newProviderName);
-
-    WebViewProviderInfo[] getValidWebViewPackages();
-
-    WebViewProviderInfo getDefaultWebViewPackage();
-
-    PackageInfo getCurrentWebViewPackage();
-
-    boolean isMultiProcessEnabled();
-
-    void enableMultiProcess(boolean enable);
-
-    void dumpState(PrintWriter pw);
-}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java
deleted file mode 100644
index 7529c41..0000000
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.webkit;
-
-import android.os.RemoteException;
-import android.os.ShellCommand;
-import android.webkit.IWebViewUpdateService;
-
-import java.io.PrintWriter;
-
-class WebViewUpdateServiceShellCommand extends ShellCommand {
-    final IWebViewUpdateService mInterface;
-
-    WebViewUpdateServiceShellCommand(IWebViewUpdateService service) {
-        mInterface = service;
-    }
-
-    @Override
-    public int onCommand(String cmd) {
-        if (cmd == null) {
-            return handleDefaultCommands(cmd);
-        }
-
-        final PrintWriter pw = getOutPrintWriter();
-        try {
-            switch(cmd) {
-                case "set-webview-implementation":
-                    return setWebViewImplementation();
-                case "enable-multiprocess":
-                    return enableMultiProcess(true);
-                case "disable-multiprocess":
-                    return enableMultiProcess(false);
-                default:
-                    return handleDefaultCommands(cmd);
-            }
-        } catch (RemoteException e) {
-            pw.println("Remote exception: " + e);
-        }
-        return -1;
-    }
-
-    private int setWebViewImplementation() throws RemoteException {
-        final PrintWriter pw = getOutPrintWriter();
-        String shellChosenPackage = getNextArg();
-        if (shellChosenPackage == null) {
-            pw.println("Failed to switch, no PACKAGE provided.");
-            pw.println("");
-            helpSetWebViewImplementation();
-            return 1;
-        }
-        String newPackage = mInterface.changeProviderAndSetting(shellChosenPackage);
-        if (shellChosenPackage.equals(newPackage)) {
-            pw.println("Success");
-            return 0;
-        } else {
-            pw.println(String.format(
-                        "Failed to switch to %s, the WebView implementation is now provided by %s.",
-                        shellChosenPackage, newPackage));
-            return 1;
-        }
-    }
-
-    private int enableMultiProcess(boolean enable) throws RemoteException {
-        final PrintWriter pw = getOutPrintWriter();
-        mInterface.enableMultiProcess(enable);
-        pw.println("Success");
-        return 0;
-    }
-
-    public void helpSetWebViewImplementation() {
-        PrintWriter pw = getOutPrintWriter();
-        pw.println("  set-webview-implementation PACKAGE");
-        pw.println("    Set the WebView implementation to the specified package.");
-    }
-
-    @Override
-    public void onHelp() {
-        PrintWriter pw = getOutPrintWriter();
-        pw.println("WebView updater commands:");
-        pw.println("  help");
-        pw.println("    Print this help text.");
-        pw.println("");
-        helpSetWebViewImplementation();
-        pw.println("  enable-multiprocess");
-        pw.println("    Enable multi-process mode for WebView");
-        pw.println("  disable-multiprocess");
-        pw.println("    Disable multi-process mode for WebView");
-        pw.println();
-    }
-}
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 af7b8d6..6964300 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -629,6 +629,9 @@
     // The locusId associated with this activity, if set.
     private LocusId mLocusId;
 
+    // Whether the activity is requesting to limit the system's educational dialogs
+    public boolean mShouldLimitSystemEducationDialogs;
+
     // Whether the activity was launched from a bubble.
     private boolean mLaunchedFromBubble;
 
@@ -6459,7 +6462,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
@@ -7300,6 +7303,16 @@
         return mLocusId;
     }
 
+    void setLimitSystemEducationDialogs(boolean limitSystemEducationDialogs) {
+        if (mShouldLimitSystemEducationDialogs == limitSystemEducationDialogs) return;
+        mShouldLimitSystemEducationDialogs = limitSystemEducationDialogs;
+        final Task task = getTask();
+        if (task != null) {
+            final boolean force = isVisibleRequested() && this == task.getTopNonFinishingActivity();
+            getTask().dispatchTaskInfoChangedIfNeeded(force);
+        }
+    }
+
     public void reportScreenCaptured() {
         if (mCaptureCallbacks != null) {
             final int n = mCaptureCallbacks.beginBroadcast();
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index fa5beca..ea6506a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -40,7 +40,6 @@
     @ChangeId
     static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L;
 
-    // TODO(b/369605358) Update EnabledSince when SDK 36 version code is available.
     /**
      * If the app's target SDK is 36+, pass-through touches from a cross-uid overlaying activity is
      * blocked by default. The activity may opt in to receive pass-through touches using
@@ -52,7 +51,7 @@
      * @see ActivityOptions#setAllowPassThroughOnTouchOutside
      */
     @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
     static final long ENABLE_OVERLAY_TOUCH_PASS_THROUGH_OPT_IN_ENFORCEMENT = 358129114L;
 
     private final ActivityRecord mActivityRecord;
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index a82aae7..3f24da9 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -190,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) {
@@ -477,7 +478,7 @@
                                 intentGrants.merge(creatorIntentGrants);
                             }
                         } catch (SecurityException securityException) {
-                            ActivityStarter.logAndThrowExceptionForIntentRedirect(
+                            ActivityStarter.logAndThrowExceptionForIntentRedirect(mService.mContext,
                                     "Creator URI Grant Caused Exception.", intent, creatorUid,
                                     creatorPackage, filterCallingUid, callingPackage,
                                     securityException);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 6b482f9..05a96d9 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -55,6 +55,7 @@
 import static android.content.pm.ActivityInfo.launchModeToString;
 import static android.os.Process.INVALID_UID;
 import static android.security.Flags.preventIntentRedirectAbortOrThrowException;
+import static android.security.Flags.preventIntentRedirectShowToast;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
@@ -105,6 +106,7 @@
 import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.Overridable;
+import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -128,12 +130,14 @@
 import android.text.TextUtils;
 import android.util.Pools.SynchronizedPool;
 import android.util.Slog;
+import android.widget.Toast;
 import android.window.RemoteTransition;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.protolog.ProtoLog;
+import com.android.server.UiThread;
 import com.android.server.am.ActivityManagerService.IntentCreatorToken;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.pm.InstantAppResolver;
@@ -614,7 +618,7 @@
             // Check if the Intent was redirected
             if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN)
                     != 0) {
-                ActivityStarter.logAndThrowExceptionForIntentRedirect(
+                logAndThrowExceptionForIntentRedirect(supervisor.mService.mContext,
                         "Unparceled intent does not have a creator token set.", intent,
                         intentCreatorUid, intentCreatorPackage, resolvedCallingUid,
                         resolvedCallingPackage, null);
@@ -650,7 +654,7 @@
                                 intentGrants.merge(creatorIntentGrants);
                             }
                         } catch (SecurityException securityException) {
-                            ActivityStarter.logAndThrowExceptionForIntentRedirect(
+                            logAndThrowExceptionForIntentRedirect(supervisor.mService.mContext,
                                     "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
                                     intentCreatorPackage, resolvedCallingUid,
                                     resolvedCallingPackage, securityException);
@@ -674,7 +678,7 @@
                                 intentGrants.merge(creatorIntentGrants);
                             }
                         } catch (SecurityException securityException) {
-                            ActivityStarter.logAndThrowExceptionForIntentRedirect(
+                            logAndThrowExceptionForIntentRedirect(supervisor.mService.mContext,
                                     "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
                                     intentCreatorPackage, resolvedCallingUid,
                                     resolvedCallingPackage, securityException);
@@ -1250,27 +1254,27 @@
                         requestCode, 0, intentCreatorUid, intentCreatorPackage, "",
                         request.ignoreTargetSecurity, inTask != null, null, resultRecord,
                         resultRootTask)) {
-                    abort = logAndAbortForIntentRedirect(
+                    abort = logAndAbortForIntentRedirect(mService.mContext,
                             "Creator checkStartAnyActivityPermission Caused abortion.",
                             intent, intentCreatorUid, intentCreatorPackage, callingUid,
                             callingPackage);
                 }
             } catch (SecurityException e) {
-                logAndThrowExceptionForIntentRedirect(
+                logAndThrowExceptionForIntentRedirect(mService.mContext,
                         "Creator checkStartAnyActivityPermission Caused Exception.",
                         intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage,
                         e);
             }
             if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid,
                     0, resolvedType, aInfo.applicationInfo)) {
-                abort = logAndAbortForIntentRedirect(
+                abort = logAndAbortForIntentRedirect(mService.mContext,
                         "Creator IntentFirewall.checkStartActivity Caused abortion.",
                         intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
             }
 
             if (!mService.getPermissionPolicyInternal().checkStartActivity(intent,
                     intentCreatorUid, intentCreatorPackage)) {
-                abort = logAndAbortForIntentRedirect(
+                abort = logAndAbortForIntentRedirect(mService.mContext,
                         "Creator PermissionPolicyService.checkStartActivity Caused abortion.",
                         intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
             }
@@ -2397,7 +2401,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;
@@ -3468,8 +3473,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) {
@@ -3595,25 +3600,38 @@
         pw.println(mInTaskFragment);
     }
 
-    static void logAndThrowExceptionForIntentRedirect(@NonNull String message,
-            @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
-            int callingUid, @Nullable String callingPackage,
+    static void logAndThrowExceptionForIntentRedirect(@NonNull Context context,
+            @NonNull String message, @NonNull Intent intent, int intentCreatorUid,
+            @Nullable String intentCreatorPackage, int callingUid, @Nullable String callingPackage,
             @Nullable SecurityException originalException) {
         String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
                 intentCreatorPackage, callingUid, callingPackage);
         Slog.wtf(TAG, msg);
+        if (preventIntentRedirectShowToast()) {
+            UiThread.getHandler().post(
+                    () -> Toast.makeText(context,
+                            "Activity launch blocked. go/report-bug-intentRedir to report a bug",
+                            Toast.LENGTH_LONG).show());
+        }
         if (preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled(
                 ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid)) {
             throw new SecurityException(msg, originalException);
         }
     }
 
-    private static boolean logAndAbortForIntentRedirect(@NonNull String message,
-            @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
-            int callingUid, @Nullable String callingPackage) {
+    private static boolean logAndAbortForIntentRedirect(@NonNull Context context,
+            @NonNull String message, @NonNull Intent intent, int intentCreatorUid,
+            @Nullable String intentCreatorPackage, int callingUid,
+            @Nullable String callingPackage) {
         String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
                 intentCreatorPackage, callingUid, callingPackage);
         Slog.wtf(TAG, msg);
+        if (preventIntentRedirectShowToast()) {
+            UiThread.getHandler().post(
+                    () -> Toast.makeText(context,
+                            "Activity launch blocked. go/report-bug-intentRedir to report a bug",
+                            Toast.LENGTH_LONG).show());
+        }
         return preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled(
                 ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 96cb2f2..7963d09 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1244,11 +1244,13 @@
                 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,
+                SafeActivityOptions.fromBundle(bOptions, callingPid, callingUid), userId, reason,
                 null /* originatingPendingIntent */, false);
     }
 
@@ -1274,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");
@@ -1283,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) {
@@ -1298,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")
@@ -1363,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);
@@ -1452,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
@@ -1576,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)
@@ -1588,7 +1596,7 @@
                 .setResultWho(resultWho)
                 .setRequestCode(requestCode)
                 .setStartFlags(startFlags)
-                .setActivityOptions(bOptions)
+                .setActivityOptions(bOptions, callingPid, callingUid)
                 .setUserId(userId)
                 .setProfilerInfo(profilerInfo)
                 .setWaitResult(res)
@@ -1603,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)
@@ -1616,7 +1625,7 @@
                 .setRequestCode(requestCode)
                 .setStartFlags(startFlags)
                 .setGlobalConfiguration(config)
-                .setActivityOptions(bOptions)
+                .setActivityOptions(bOptions, callingPid, callingUid)
                 .setUserId(userId)
                 .execute();
     }
@@ -1820,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,
@@ -1846,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");
 
@@ -1859,7 +1872,7 @@
                     .setCallingPackage(intent.getPackage())
                     .setCallingFeatureId(callingFeatureId)
                     .setUserId(userId)
-                    .setActivityOptions(activityOptions.toBundle())
+                    .setActivityOptions(safeOptions)
                     .setRealCallingUid(Binder.getCallingUid())
                     .execute();
         } finally {
@@ -2228,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));
         }
     }
 
@@ -3898,6 +3913,17 @@
     }
 
     @Override
+    public void setLimitSystemEducationDialogs(
+            IBinder appToken, boolean limitSystemEducationDialogs) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInRootTaskLocked(appToken);
+            if (r != null) {
+                r.setLimitSystemEducationDialogs(limitSystemEducationDialogs);
+            }
+        }
+    }
+
+    @Override
     public boolean updateConfiguration(Configuration values) {
         mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
 
@@ -5881,6 +5907,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.
      */
@@ -5894,7 +5943,7 @@
             options.setPendingIntentBackgroundActivityStartMode(
                     ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
         }
-        return new SafeActivityOptions(options);
+        return new SafeActivityOptions(options, Binder.getCallingPid(), Binder.getCallingUid());
     }
 
     /**
@@ -6059,10 +6108,12 @@
                 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 */,
                     false);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 18b23ad..30b53d1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2525,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. */
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
index f1dd41e..90c0866 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
@@ -37,6 +37,7 @@
 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_POSITION_MULTIPLIER_CENTER;
 import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
 import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
+import static com.android.server.wm.AppCompatUtils.isDisplayIgnoreActivitySizeRestrictions;
 
 import android.annotation.NonNull;
 import android.content.pm.IPackageManager;
@@ -175,8 +176,17 @@
                 && mActivityRecord.mDisplayContent.getIgnoreOrientationRequest();
     }
 
+    /**
+     * Whether to ignore fixed orientation, aspect ratio and resizability of activity.
+     */
     boolean hasFullscreenOverride() {
-        return shouldApplyUserFullscreenOverride() || isSystemOverrideToFullscreenEnabled();
+        return shouldApplyUserFullscreenOverride() || isSystemOverrideToFullscreenEnabled()
+                || shouldIgnoreActivitySizeRestrictionsForDisplay();
+    }
+
+    boolean shouldIgnoreActivitySizeRestrictionsForDisplay() {
+        return isDisplayIgnoreActivitySizeRestrictions(mActivityRecord)
+                && !mAllowOrientationOverrideOptProp.isFalse();
     }
 
     float getUserMinAspectRatio() {
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index e3a9d67..7aed33d 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -53,11 +53,16 @@
 
     @ActivityInfo.ScreenOrientation
     int overrideOrientationIfNeeded(@ActivityInfo.ScreenOrientation int candidate) {
+        final AppCompatAspectRatioOverrides aspectRatioOverrides =
+                mAppCompatOverrides.getAppCompatAspectRatioOverrides();
+        // Ignore all orientation requests of activities for eligible virtual displays.
+        if (aspectRatioOverrides.shouldIgnoreActivitySizeRestrictionsForDisplay()) {
+            return SCREEN_ORIENTATION_USER;
+        }
         final DisplayContent displayContent = mActivityRecord.mDisplayContent;
         final boolean isIgnoreOrientationRequestEnabled = displayContent != null
                 && displayContent.getIgnoreOrientationRequest();
-        final boolean hasFullscreenOverride = mAppCompatOverrides
-                .getAppCompatAspectRatioOverrides().hasFullscreenOverride();
+        final boolean hasFullscreenOverride = aspectRatioOverrides.hasFullscreenOverride();
         final boolean shouldCameraCompatControlOrientation =
                 AppCompatCameraPolicy.shouldCameraCompatControlOrientation(mActivityRecord);
         if (hasFullscreenOverride && isIgnoreOrientationRequestEnabled
@@ -76,8 +81,8 @@
         // In some cases (e.g. Kids app) we need to map the candidate orientation to some other
         // orientation.
         candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate);
-        final boolean shouldApplyUserMinAspectRatioOverride = mAppCompatOverrides
-                .getAppCompatAspectRatioOverrides().shouldApplyUserMinAspectRatioOverride();
+        final boolean shouldApplyUserMinAspectRatioOverride = aspectRatioOverrides
+                .shouldApplyUserMinAspectRatioOverride();
         if (shouldApplyUserMinAspectRatioOverride && (!isFixedOrientation(candidate)
                 || candidate == SCREEN_ORIENTATION_LOCKED)) {
             Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate)
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
index f069dcd..d0d3d43 100644
--- a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -149,7 +149,7 @@
             @NonNull Configuration newParentConfig) {
         mSizeCompatScale = mActivityRecord.mAppCompatController.getTransparentPolicy()
                 .findOpaqueNotFinishingActivityBelow()
-                .map(activityRecord -> mSizeCompatScale)
+                .map(ar -> Math.min(1.0f, ar.getCompatScale()))
                 .orElseGet(() -> calculateSizeCompatScale(
                         resolvedAppBounds, containerAppBounds, newParentConfig));
     }
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 8d84248..db76eb9 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -32,6 +32,8 @@
 import android.view.InsetsState;
 import android.view.WindowInsets;
 
+import com.android.window.flags.Flags;
+
 import java.util.function.BooleanSupplier;
 
 /**
@@ -86,10 +88,22 @@
     /**
      * @param activityRecord The {@link ActivityRecord} for the app package.
      * @param overrideChangeId The per-app override identifier.
-     * @return {@code true} if the per-app override is enable for the given activity.
+     * @return {@code true} if the per-app override is enable for the given activity and the
+     * display does not ignore fixed orientation, aspect ratio and resizability of activity.
      */
     static boolean isChangeEnabled(@NonNull ActivityRecord activityRecord, long overrideChangeId) {
-        return activityRecord.info.isChangeEnabled(overrideChangeId);
+        return activityRecord.info.isChangeEnabled(overrideChangeId)
+                && !isDisplayIgnoreActivitySizeRestrictions(activityRecord);
+    }
+
+    /**
+     * Whether the display ignores fixed orientation, aspect ratio and resizability of activities.
+     */
+    static boolean isDisplayIgnoreActivitySizeRestrictions(
+            @NonNull ActivityRecord activityRecord) {
+        final DisplayContent dc = activityRecord.mDisplayContent;
+        return Flags.vdmForceAppUniversalResizableApi() && dc != null
+                && dc.isDisplayIgnoreActivitySizeRestrictions();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index ef34dab..c845c50 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -175,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..2fe023e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -107,6 +107,12 @@
         mNavigationMonitor.onFocusWindowChanged(newFocus);
     }
 
+    void onEmbeddedWindowGestureTransferred(@NonNull WindowState host) {
+        if (Flags.disallowAppProgressEmbeddedWindow()) {
+            mNavigationMonitor.onEmbeddedWindowGestureTransferred(host);
+        }
+    }
+
     /**
      * Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
      * back gesture animation.
@@ -178,6 +184,9 @@
                 return null;
             }
 
+            final ArrayList<EmbeddedWindowController.EmbeddedWindow> embeddedWindows = wmService
+                    .mEmbeddedWindowController.getByHostWindow(window);
+
             currentActivity = window.mActivityRecord;
             currentTask = window.getTask();
             if ((currentTask != null && !currentTask.isVisibleRequested())
@@ -199,11 +208,22 @@
             infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
             infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
             infoBuilder.setTouchableRegion(window.getFrame());
-            infoBuilder.setAppProgressAllowed((window.getAttrs().privateFlags
-                    & PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED) != 0);
             if (currentTask != null) {
                 infoBuilder.setFocusedTaskId(currentTask.mTaskId);
             }
+            boolean transferGestureToEmbedded = false;
+            if (Flags.disallowAppProgressEmbeddedWindow() && embeddedWindows != null) {
+                for (int i = embeddedWindows.size() - 1; i >= 0; --i) {
+                    if (embeddedWindows.get(i).mGestureToEmbedded) {
+                        transferGestureToEmbedded = true;
+                        break;
+                    }
+                }
+            }
+            final boolean canInterruptInView = (window.getAttrs().privateFlags
+                    & PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED) != 0;
+            infoBuilder.setAppProgressAllowed(canInterruptInView && !transferGestureToEmbedded
+                    && callbackInfo.isAnimationCallback());
             mNavigationMonitor.startMonitor(window, navigationObserver);
 
             ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
@@ -742,6 +762,20 @@
         }
 
         /**
+         * Notify focus window has transferred touch gesture to embedded window. Shell should pilfer
+         * pointers so embedded process won't receive motion event.
+         *
+         */
+        void onEmbeddedWindowGestureTransferred(@NonNull WindowState host) {
+            if (!isMonitorForRemote() || host != mNavigatingWindow) {
+                return;
+            }
+            final Bundle result = new Bundle();
+            result.putBoolean(BackNavigationInfo.KEY_TOUCH_GESTURE_TRANSFERRED, true);
+            mObserver.sendResult(result);
+        }
+
+        /**
          * Notify an unexpected transition has happened during back navigation.
          */
         private void onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening,
@@ -1726,8 +1760,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 +1785,6 @@
         }
 
         class ScheduleAnimationBuilder {
-            final int mType;
             final BackAnimationAdapter mBackAnimationAdapter;
             final NavigationMonitor mNavigationMonitor;
             WindowContainer mCloseTarget;
@@ -1760,9 +1792,8 @@
             boolean mIsLaunchBehind;
             TaskSnapshot mSnapshot;
 
-            ScheduleAnimationBuilder(int type, BackAnimationAdapter adapter,
+            ScheduleAnimationBuilder(BackAnimationAdapter adapter,
                     NavigationMonitor monitor) {
-                mType = type;
                 mBackAnimationAdapter = adapter;
                 mNavigationMonitor = monitor;
             }
@@ -1793,7 +1824,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 +2050,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 +2064,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 3e553ad..ec171c5 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -49,9 +49,9 @@
 import static com.android.window.flags.Flags.balImproveRealCallerVisibilityCheck;
 import static com.android.window.flags.Flags.balImprovedMetrics;
 import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
-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.balStrictModeRo;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 import static java.util.Objects.requireNonNull;
@@ -63,21 +63,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 +96,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 +135,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 +147,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)
@@ -350,7 +358,7 @@
             } else if (mIsCallForResult) {
                 mAutoOptInReason = AUTO_OPT_IN_CALL_FOR_RESULT;
                 mAutoOptInCaller = false;
-            } else if (callingUid == realCallingUid && !balRequireOptInSameUid()) {
+            } else if (callingUid == realCallingUid) {
                 mAutoOptInReason = AUTO_OPT_IN_SAME_UID;
                 mAutoOptInCaller = false;
             } else if (realCallerBackgroundActivityStartMode
@@ -595,7 +603,6 @@
                     .append(balImproveRealCallerVisibilityCheck());
             sb.append("; balRequireOptInByPendingIntentCreator: ")
                     .append(balRequireOptInByPendingIntentCreator());
-            sb.append("; balRequireOptInSameUid: ").append(balRequireOptInSameUid());
             sb.append("; balRespectAppSwitchStateWhenCheckBoundByForegroundUid: ")
                     .append(balRespectAppSwitchStateWhenCheckBoundByForegroundUid());
             sb.append("; balDontBringExistingBackgroundTaskStackToFg: ")
@@ -841,7 +848,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 (balStrictModeRo()) {
+            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());
+            }
+        }
     }
 
     /**
@@ -1042,8 +1162,9 @@
      * or {@link #BAL_BLOCK} if the launch should be blocked
      */
     BalVerdict checkBackgroundActivityStartAllowedByRealCallerInBackground(BalState state) {
-        if (state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
-                == MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+        boolean allowAlways = state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+                == MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
+        if (allowAlways
                 && hasBalPermission(state.mRealCallingUid, state.mRealCallingPid)) {
             return new BalVerdict(BAL_ALLOW_PERMISSION,
                     /*background*/ false,
@@ -1055,8 +1176,7 @@
                 + state.mRealCallingPid + ", " + state.mRealCallingPackage + ") "
                 + balStartModeToString(
                 state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
-        if (state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
-                == MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+        if (allowAlways
                 && mService.hasSystemAlertWindowPermission(state.mRealCallingUid,
                 state.mRealCallingPid, state.mRealCallingPackage)) {
             Slog.w(
@@ -1070,7 +1190,7 @@
 
         // if the realCallingUid is a persistent system process, abort if the IntentSender
         // wasn't allowed to start an activity
-        if (state.mAllowBalExemptionForSystemProcess
+        if ((allowAlways || state.mAllowBalExemptionForSystemProcess)
                 && state.mIsRealCallingUidPersistentSystemProcess) {
             return new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID,
                     /*background*/ false,
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index cbe3d79..6f8c17a 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -402,6 +402,7 @@
                 || first.modeId != second.modeId
                 || first.renderFrameRate != second.renderFrameRate
                 || first.hasArrSupport != second.hasArrSupport
+                || !Objects.equals(first.frameRateCategoryRate, second.frameRateCategoryRate)
                 || first.defaultModeId != second.defaultModeId
                 || first.userPreferredModeId != second.userPreferredModeId
                 || !Arrays.equals(first.supportedModes, second.supportedModes)
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..e827f44 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5789,6 +5789,17 @@
     }
 
     /**
+     * Checks if this display is allowed to ignore fixed orientation, aspect ratio,
+     * and resizability of apps.
+     *
+     * <p>This can be set via
+     * {@link VirtualDisplayConfig.Builder#setIgnoreActivitySizeRestrictions}.</p>
+     */
+    boolean isDisplayIgnoreActivitySizeRestrictions() {
+        return mWmService.mDisplayWindowSettings.isIgnoreActivitySizeRestrictionsLocked(this);
+    }
+
+    /**
      * The direct child layer of the display to put all non-overlay windows. This is also used for
      * screen rotation animation so that there is a parent layer to put the animation leash.
      */
@@ -6923,27 +6934,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/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index e585efa..c87b811 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -275,6 +275,24 @@
         mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
     }
 
+    boolean isIgnoreActivitySizeRestrictionsLocked(@NonNull DisplayContent dc) {
+        final DisplayInfo displayInfo = dc.getDisplayInfo();
+        final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
+        return settings.mIgnoreActivitySizeRestrictions != null
+                && settings.mIgnoreActivitySizeRestrictions;
+    }
+
+    void setIgnoreActivitySizeRestrictionsOnDisplayLocked(@NonNull String displayUniqueId,
+            int displayType, boolean enabled) {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.uniqueId = displayUniqueId;
+        displayInfo.type = displayType;
+        final SettingsProvider.SettingsEntry overrideSettings =
+                mSettingsProvider.getOverrideSettings(displayInfo);
+        overrideSettings.mIgnoreActivitySizeRestrictions = enabled;
+        mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+    }
+
     void clearDisplaySettings(@NonNull String displayUniqueId, int displayType) {
         final DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.uniqueId = displayUniqueId;
@@ -474,6 +492,8 @@
             Boolean mIgnoreDisplayCutout;
             @Nullable
             Boolean mDontMoveToTop;
+            @Nullable
+            Boolean mIgnoreActivitySizeRestrictions;
 
             SettingsEntry() {}
 
@@ -557,6 +577,11 @@
                     mDontMoveToTop = other.mDontMoveToTop;
                     changed = true;
                 }
+                if (!Objects.equals(other.mIgnoreActivitySizeRestrictions,
+                        mIgnoreActivitySizeRestrictions)) {
+                    mIgnoreActivitySizeRestrictions = other.mIgnoreActivitySizeRestrictions;
+                    changed = true;
+                }
                 return changed;
             }
 
@@ -649,6 +674,11 @@
                     mDontMoveToTop = delta.mDontMoveToTop;
                     changed = true;
                 }
+                if (delta.mIgnoreActivitySizeRestrictions != null && !Objects.equals(
+                        delta.mIgnoreActivitySizeRestrictions, mIgnoreActivitySizeRestrictions)) {
+                    mIgnoreActivitySizeRestrictions = delta.mIgnoreActivitySizeRestrictions;
+                    changed = true;
+                }
                 return changed;
             }
 
@@ -667,7 +697,8 @@
                         && mFixedToUserRotation == null
                         && mIgnoreOrientationRequest == null
                         && mIgnoreDisplayCutout == null
-                        && mDontMoveToTop == null;
+                        && mDontMoveToTop == null
+                        && mIgnoreActivitySizeRestrictions == null;
             }
 
             @Override
@@ -691,7 +722,9 @@
                         && Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
                         && Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
                         && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout)
-                        && Objects.equals(mDontMoveToTop, that.mDontMoveToTop);
+                        && Objects.equals(mDontMoveToTop, that.mDontMoveToTop)
+                        && Objects.equals(mIgnoreActivitySizeRestrictions,
+                                that.mIgnoreActivitySizeRestrictions);
             }
 
             @Override
@@ -700,7 +733,7 @@
                         mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
                         mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mIsHomeSupported,
                         mImePolicy, mFixedToUserRotation, mIgnoreOrientationRequest,
-                        mIgnoreDisplayCutout, mDontMoveToTop);
+                        mIgnoreDisplayCutout, mDontMoveToTop, mIgnoreActivitySizeRestrictions);
             }
 
             @Override
@@ -722,6 +755,7 @@
                         + ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
                         + ", mIgnoreDisplayCutout=" + mIgnoreDisplayCutout
                         + ", mDontMoveToTop=" + mDontMoveToTop
+                        + ", mForceAppsUniversalResizable=" + mIgnoreActivitySizeRestrictions
                         + '}';
             }
         }
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 5ac4cf8..907d0dc 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -39,6 +39,8 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.server.input.InputManagerService;
 
+import java.util.ArrayList;
+
 /**
  * Keeps track of embedded windows.
  *
@@ -146,6 +148,20 @@
         return mWindowsByWindowToken.get(windowToken);
     }
 
+    @Nullable ArrayList<EmbeddedWindow> getByHostWindow(WindowState host) {
+        ArrayList<EmbeddedWindow> windows = null;
+        for (int i = mWindows.size() - 1; i >= 0; i--) {
+            final EmbeddedWindow ew = mWindows.valueAt(i);
+            if (ew.mHostWindowState == host) {
+                if (windows == null) {
+                    windows = new ArrayList<>();
+                }
+                windows.add(ew);
+            }
+        }
+        return windows;
+    }
+
     private boolean isValidTouchGestureParams(WindowState hostWindowState,
             EmbeddedWindow embeddedWindow) {
         if (embeddedWindow == null) {
@@ -191,8 +207,12 @@
             throw new SecurityException(
                     "Transfer request must originate from owner of transferFromToken");
         }
-        return mInputManagerService.transferTouchGesture(ew.getInputChannelToken(),
-                transferToHostWindowState.mInputChannelToken);
+        final boolean didTransfer = mInputManagerService.transferTouchGesture(
+                ew.getInputChannelToken(), transferToHostWindowState.mInputChannelToken);
+        if (didTransfer) {
+            ew.mGestureToEmbedded = false;
+        }
+        return didTransfer;
     }
 
     boolean transferToEmbedded(int callingUid, WindowState hostWindowState,
@@ -205,8 +225,15 @@
             throw new SecurityException(
                     "Transfer request must originate from owner of transferFromToken");
         }
-        return mInputManagerService.transferTouchGesture(hostWindowState.mInputChannelToken,
+        final boolean didTransfer = mInputManagerService.transferTouchGesture(
+                hostWindowState.mInputChannelToken,
                 ew.getInputChannelToken());
+        if (didTransfer) {
+            ew.mGestureToEmbedded = true;
+            mAtmService.mBackNavigationController.onEmbeddedWindowGestureTransferred(
+                    hostWindowState);
+        }
+        return didTransfer;
     }
 
     static class EmbeddedWindow implements InputTarget {
@@ -235,6 +262,9 @@
         // the host window.
         private @WindowInsets.Type.InsetsType int mRequestedVisibleTypes = 0;
 
+        /** Whether the gesture is transferred to embedded window. */
+        boolean mGestureToEmbedded = false;
+
         /**
          * @param session  calling session to check ownership of the window
          * @param clientToken client token used to clean up the map if the embedding process dies
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/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..e13577c 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();
         }
 
@@ -3420,6 +3423,8 @@
                 ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID;
         info.shouldDockBigOverlays = top != null && top.shouldDockBigOverlays;
         info.mTopActivityLocusId = top != null ? top.getLocusId() : null;
+        info.isTopActivityLimitSystemEducationDialogs = top != null
+              ? top.mShouldLimitSystemEducationDialogs : false;
         final Task parentTask = getParent() != null ? getParent().asTask() : null;
         info.parentTaskId = parentTask != null && parentTask.mCreatedByOrganizer
                 ? parentTask.mTaskId
@@ -3461,7 +3466,8 @@
             return null;
         }
         final Rect windowFrame = mainWindow.getFrame();
-        if (top.getBounds().equals(windowFrame)) {
+        final Rect parentFrame = mainWindow.getParentFrame();
+        if (parentFrame.equals(windowFrame)) {
             return null;
         }
         return windowFrame;
@@ -3913,8 +3919,10 @@
             sb.append(affinityIntent.getComponent().flattenToShortString());
         }
         sb.append(" isResizeable=").append(isResizeable());
-        sb.append(" minWidth=").append(mMinWidth);
-        sb.append(" minHeight=").append(mMinHeight);
+        if (mMinWidth != INVALID_MIN_SIZE || mMinHeight != INVALID_MIN_SIZE) {
+            sb.append(" minWidth=").append(mMinWidth);
+            sb.append(" minHeight=").append(mMinHeight);
+        }
         sb.append('}');
         return stringName = sb.toString();
     }
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..454e431 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -237,15 +237,16 @@
     private final ArraySet<WindowToken> mVisibleAtTransitionEndTokens = new ArraySet<>();
 
     /**
-     * Set of transient activities (lifecycle initially tied to this transition) and their
+     * Map of transient activities (lifecycle initially tied to this transition) to their
      * restore-below tasks.
      */
     private ArrayMap<ActivityRecord, Task> mTransientLaunches = null;
 
     /**
      * The tasks that may be occluded by the transient activity. Assume the task stack is
-     * [Home, A(opaque), B(opaque), C(translucent)] (bottom to top), then A is the restore-below
-     * task, and [B, C] are the transient-hide tasks.
+     * [Home, A(opaque), B(opaque), C(translucent)] (bottom to top), and Home is started in a
+     * transient-launch activity, then A is the restore-below task, and [B, C] are the
+     * transient-hide tasks.
      */
     private ArrayList<Task> mTransientHideTasks;
 
@@ -401,18 +402,26 @@
         mTransientLaunches.put(activity, restoreBelow);
         setTransientLaunchToChanges(activity);
 
-        final Task transientRootTask = activity.getRootTask();
+        final int restoreBelowTaskId = restoreBelow != null ? restoreBelow.mTaskId : -1;
+        ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
+                + "transient-launch restoreBelowTaskId=%d", mSyncId, activity, restoreBelowTaskId);
+
+        final Task transientLaunchRootTask = activity.getRootTask();
         final WindowContainer<?> parent = restoreBelow != null ? restoreBelow.getParent()
-                : (transientRootTask != null ? transientRootTask.getParent() : null);
+                : (transientLaunchRootTask != null ? transientLaunchRootTask.getParent() : null);
         if (parent != null) {
             // Collect all visible tasks which can be occluded by the transient activity to
             // make sure they are in the participants so their visibilities can be updated when
             // finishing transition.
+            // Note: This currently assumes that the parent is a DA containing the full set of
+            //       visible tasks
             parent.forAllTasks(t -> {
                 // Skip transient-launch task
-                if (t == transientRootTask) return false;
+                if (t == transientLaunchRootTask) return false;
                 if (t.isVisibleRequested() && !t.isAlwaysOnTop()) {
                     if (t.isRootTask()) {
+                        ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
+                                "  transient hide: taskId=%d", t.mTaskId);
                         mTransientHideTasks.add(t);
                     }
                     if (t.isLeafTask()) {
@@ -442,9 +451,6 @@
             // the gesture threshold.
             activity.getTask().setCanAffectSystemUiFlags(false);
         }
-
-        ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
-                + "transient-launch", mSyncId, activity);
     }
 
     /** @return whether `wc` is a descendent of a transient-hide window. */
@@ -462,6 +468,8 @@
     /** Returns {@code true} if the task should keep visible if this is a transient transition. */
     boolean isTransientVisible(@NonNull Task task) {
         if (mTransientLaunches == null) return false;
+
+        // Check if all the transient-launch activities are occluded
         int occludedCount = 0;
         final int numTransient = mTransientLaunches.size();
         for (int i = numTransient - 1; i >= 0; --i) {
@@ -486,6 +494,8 @@
             // Let transient-hide activities pause before transition is finished.
             return false;
         }
+
+        // If this task is currently transient-hide, then keep it visible
         return isInTransientHide(task);
     }
 
@@ -557,6 +567,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);
@@ -600,7 +618,8 @@
     }
 
     /**
-     * Only set flag to the parent tasks and activity itself.
+     * Sets the FLAG_TRANSIENT_LAUNCH flag to all changes associated with the given activity
+     * container and parent tasks.
      */
     private void setTransientLaunchToChanges(@NonNull WindowContainer wc) {
         for (WindowContainer curr = wc; curr != null && mChanges.containsKey(curr);
@@ -766,7 +785,7 @@
         // Only look at tasks, taskfragments, or activities
         if (wc.asTaskFragment() == null && wc.asActivityRecord() == null) return;
         if (!isInTransientHide(wc)) return;
-        info.mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH;
+        info.mFlags |= ChangeInfo.FLAG_TRANSIENT_HIDE;
     }
 
     private void recordDisplay(DisplayContent dc) {
@@ -2440,6 +2459,13 @@
         sb.append(" type=" + transitTypeToString(mType));
         sb.append(" flags=0x" + Integer.toHexString(mFlags));
         sb.append(" overrideAnimOptions=" + mOverrideOptions);
+        if (!mChanges.isEmpty()) {
+            sb.append(" c=[");
+            for (int i = 0; i < mChanges.size(); i++) {
+                sb.append("\n").append("   ").append(mChanges.valueAt(i).toString());
+            }
+            sb.append("\n]\n");
+        }
         sb.append('}');
         return sb.toString();
     }
@@ -3388,8 +3414,14 @@
          * seamless rotation. This is currently only used by DisplayContent during fixed-rotation.
          */
         private static final int FLAG_SEAMLESS_ROTATION = 1;
+        /**
+         * Identifies the associated WindowContainer as a transient-launch task or activity.
+         */
         private static final int FLAG_TRANSIENT_LAUNCH = 2;
-        private static final int FLAG_ABOVE_TRANSIENT_LAUNCH = 4;
+        /**
+         * Identifies the associated WindowContainer as a transient-hide task or activity.
+         */
+        private static final int FLAG_TRANSIENT_HIDE = 4;
 
         /** This container explicitly requested no-animation (usually Activity level). */
         private static final int FLAG_CHANGE_NO_ANIMATION = 0x8;
@@ -3405,18 +3437,32 @@
         /** 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,
                 FLAG_TRANSIENT_LAUNCH,
-                FLAG_ABOVE_TRANSIENT_LAUNCH,
+                FLAG_TRANSIENT_HIDE,
                 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 {}
+        @interface ChangeInfoFlag {}
 
         @NonNull final WindowContainer mContainer;
         /**
@@ -3445,7 +3491,7 @@
         @ActivityInfo.Config int mKnownConfigChanges;
 
         /** Extra information about this change. */
-        @Flag int mFlags = FLAG_NONE;
+        @ChangeInfoFlag int mFlags = FLAG_NONE;
 
         /** Snapshot surface and luma, if relevant. */
         SurfaceControl mSnapshot;
@@ -3480,14 +3526,22 @@
 
         @Override
         public String toString() {
-            return mContainer.toString();
+            StringBuilder sb = new StringBuilder(64);
+            sb.append("ChangeInfo{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(" container=").append(mContainer);
+            sb.append(" flags=0x").append(Integer.toHexString(mFlags));
+            sb.append('}');
+            return sb.toString();
         }
 
         boolean hasChanged() {
             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_TRANSIENT_HIDE) != 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
@@ -3506,9 +3560,12 @@
 
         @TransitionInfo.TransitionMode
         int getTransitMode(@NonNull WindowContainer wc) {
-            if ((mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) {
+            if ((mFlags & ChangeInfo.FLAG_TRANSIENT_HIDE) != 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 +3597,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/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java
index 85a118d..edd9924 100644
--- a/services/core/java/com/android/server/wm/TransparentPolicy.java
+++ b/services/core/java/com/android/server/wm/TransparentPolicy.java
@@ -196,10 +196,11 @@
 
     // We evaluate the case when the policy should not be applied.
     private boolean shouldSkipTransparentPolicy(@Nullable ActivityRecord opaqueActivity) {
-        if (opaqueActivity == null || opaqueActivity.isEmbedded()) {
+        if (opaqueActivity == null || opaqueActivity.isEmbedded()
+                || !opaqueActivity.areBoundsLetterboxed()) {
             // We skip letterboxing if the translucent activity doesn't have any
             // opaque activities beneath or the activity below is embedded which
-            // never has letterbox.
+            // never has letterbox or the activity is not letterboxed at all.
             return true;
         }
         final AppCompatSizeCompatModePolicy scmPolicy = mActivityRecord.mAppCompatController
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6c92ae6..e0c473d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1117,7 +1117,9 @@
      */
     void onDisplayChanged(DisplayContent dc) {
         if (mDisplayContent != null && mDisplayContent != dc) {
-            mTransitionController.collect(this);
+            if (asWindowState() == null) {
+                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);
@@ -2119,7 +2121,8 @@
     }
 
     /**
-     * For all tasks at or below this container call the callback.
+     * Calls the given {@param callback} for all tasks in depth-first top-down z-order at or below
+     * this container.
      *
      * @param callback Calls the {@link ToBooleanFunction#apply} method for each task found and
      *                 stops the search if {@link ToBooleanFunction#apply} returns {@code true}.
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 82d39a3..ce032b4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -859,6 +859,18 @@
     public abstract boolean isHomeSupportedOnDisplay(int displayId);
 
     /**
+     * Sets whether the relevant display content ignores fixed orientation, aspect ratio
+     * and resizability of apps.
+     *
+     * @param displayUniqueId The unique ID of the display. Note that the display may not yet be
+     *   created, but whenever it is, this property will be applied.
+     * @param displayType The type of the display, e.g. {@link Display#TYPE_VIRTUAL}.
+     * @param enabled Whether app is universal resizable on this display.
+     */
+    public abstract void setIgnoreActivitySizeRestrictionsOnDisplay(
+            @NonNull String displayUniqueId, int displayType, boolean enabled);
+
+    /**
      * Removes any settings relevant to the given display.
      *
      * <p>This may be used when a property is set for a display unique ID before the display
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f4ad030..88b2d22 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);
         }
@@ -8382,6 +8384,20 @@
         }
 
         @Override
+        public void setIgnoreActivitySizeRestrictionsOnDisplay(@NonNull String displayUniqueId,
+                int displayType, boolean enabled) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                synchronized (mGlobalLock) {
+                    mDisplayWindowSettings.setIgnoreActivitySizeRestrictionsOnDisplayLocked(
+                            displayUniqueId, displayType, enabled);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+
+        @Override
         public void clearDisplaySettings(String displayUniqueId, int displayType) {
             final long origId = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6141876..d01e29b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -258,6 +258,7 @@
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.RefreshRatePolicy.FrameRateVote;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.server.wm.utils.RegionUtils;
 import com.android.window.flags.Flags;
 
 import dalvik.annotation.optimization.NeverCompile;
@@ -2327,7 +2328,8 @@
             if (mStartingData != null && mStartingData.mAssociatedTask == null
                     && mTempConfiguration.windowConfiguration.getRotation()
                             == selfConfiguration.windowConfiguration.getRotation()
-                    && !mTempConfiguration.windowConfiguration.getBounds().equals(getBounds())) {
+                    && !RegionUtils.sizeEquals(
+                            mTempConfiguration.windowConfiguration.getBounds(), getBounds())) {
                 mStartingData.mResizedFromTransfer = true;
                 // Lock the starting window to task, so it won't resize from transfer anymore.
                 mActivityRecord.associateStartingWindowWithTaskIfNeeded();
@@ -2862,12 +2864,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/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java
index ff23145..6c5da17 100644
--- a/services/core/java/com/android/server/wm/utils/RegionUtils.java
+++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java
@@ -92,4 +92,9 @@
         }
         return area;
     }
+
+    /** Returns whether the sizes between the two Rects are equal. */
+    public static boolean sizeEquals(Rect a, Rect b) {
+        return a.width() == b.width() && a.height() == b.height();
+    }
 }
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 59dbf28..0ecc0a8 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -74,12 +74,10 @@
     jfieldID duration;
 } sRampClassInfo;
 static struct {
-    jfieldID startAmplitude;
-    jfieldID endAmplitude;
-    jfieldID startFrequencyHz;
-    jfieldID endFrequencyHz;
-    jfieldID duration;
-} sPwleClassInfo;
+    jfieldID amplitude;
+    jfieldID frequencyHz;
+    jfieldID timeMillis;
+} sPwlePointClassInfo;
 
 static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
               static_cast<uint8_t>(Aidl::EffectStrength::LIGHT));
@@ -191,10 +189,11 @@
 
 static Aidl::PwleV2Primitive pwleV2PrimitiveFromJavaPrimitive(JNIEnv* env, jobject pwleObj) {
     Aidl::PwleV2Primitive pwle;
-    pwle.amplitude = static_cast<float>(env->GetFloatField(pwleObj, sPwleClassInfo.endAmplitude));
+    pwle.amplitude = static_cast<float>(env->GetFloatField(pwleObj, sPwlePointClassInfo.amplitude));
     pwle.frequencyHz =
-            static_cast<float>(env->GetFloatField(pwleObj, sPwleClassInfo.endFrequencyHz));
-    pwle.timeMillis = static_cast<int32_t>(env->GetIntField(pwleObj, sPwleClassInfo.duration));
+            static_cast<float>(env->GetFloatField(pwleObj, sPwlePointClassInfo.frequencyHz));
+    pwle.timeMillis =
+            static_cast<int32_t>(env->GetIntField(pwleObj, sPwlePointClassInfo.timeMillis));
     return pwle;
 }
 
@@ -620,7 +619,7 @@
          (void*)vibratorPerformComposedEffect},
         {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J",
          (void*)vibratorPerformPwleEffect},
-        {"performPwleV2Effect", "(J[Landroid/os/vibrator/PwleSegment;J)J",
+        {"performPwleV2Effect", "(J[Landroid/os/vibrator/PwlePoint;J)J",
          (void*)vibratorPerformPwleV2Effect},
         {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
         {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
@@ -647,12 +646,10 @@
     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 pwlePointClass = FindClassOrDie(env, "android/os/vibrator/PwlePoint");
+    sPwlePointClassInfo.amplitude = GetFieldIDOrDie(env, pwlePointClass, "mAmplitude", "F");
+    sPwlePointClassInfo.frequencyHz = GetFieldIDOrDie(env, pwlePointClass, "mFrequencyHz", "F");
+    sPwlePointClassInfo.timeMillis = GetFieldIDOrDie(env, pwlePointClass, "mTimeMillis", "I");
 
     jclass frequencyProfileLegacyClass =
             FindClassOrDie(env, "android/os/VibratorInfo$FrequencyProfileLegacy");
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7eb0c42..3805c02 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -191,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;
@@ -2587,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/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
index 15c9b9f..a4546ae 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionUpgrade.kt
@@ -297,6 +297,7 @@
 
         private const val MASK_ANY_FIXED =
             PermissionFlags.USER_SET or
+                PermissionFlags.ONE_TIME or
                 PermissionFlags.USER_FIXED or
                 PermissionFlags.POLICY_FIXED or
                 PermissionFlags.SYSTEM_FIXED
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 42c171b..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;
@@ -88,10 +90,11 @@
                 createAndUploadReport(sSelfService);
             }
             if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
-                boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
                 boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
                 if (isADB) {
-                    Log.d(LOG_TAG, "Received broadcast that ADB became " + connected);
+                    boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+                    Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
+                            + ", was " + mAdbActive);
                     mAdbActive = connected;
                 }
             }
@@ -117,9 +120,6 @@
         mUploadEnabled =
             context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled);
 
-        // TODO: ADB might already be active when our service started.
-        mAdbActive = false;
-
         final IntentFilter filter = new IntentFilter();
         filter.addAction(INTENT_UPLOAD_PROFILES);
         filter.addAction(UsbManager.ACTION_USB_STATE);
@@ -140,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;
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/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/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index d4b57f1..69714f3 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -26,6 +26,8 @@
 import android.net.Uri
 import android.os.Bundle
 import android.os.Parcelable
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
 import android.util.ArraySet
 import android.util.SparseArray
 import android.util.SparseIntArray
@@ -47,14 +49,19 @@
 import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
+import org.junit.Rule
 import java.security.KeyPairGenerator
 import java.security.PublicKey
 import java.util.UUID
 import kotlin.contracts.ExperimentalContracts
 
 @ExperimentalContracts
+@EnableFlags(android.content.pm.Flags.FLAG_INCLUDE_FEATURE_FLAGS_IN_PACKAGE_CACHER)
 class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, PackageImpl::class) {
 
+    @get:Rule
+    val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
     companion object {
         private val TEST_UUID = UUID.fromString("57554103-df3e-4475-ae7a-8feba49353ac")
     }
@@ -93,6 +100,8 @@
         "getUsesOptionalLibrariesSorted",
         "getUsesSdkLibrariesSorted",
         "getUsesStaticLibrariesSorted",
+        "readFeatureFlagState",
+        "writeFeatureFlagState",
         // Tested through setting minor/major manually
         "setLongVersionCode",
         "getLongVersionCode",
@@ -149,6 +158,10 @@
         "isSystem",
         "isSystemExt",
         "isVendor",
+
+        // Tested through addFeatureFlag
+        "addFeatureFlag",
+        "getFeatureFlagState",
     )
 
     override val baseParams = listOf(
@@ -275,6 +288,7 @@
         AndroidPackage::isUpdatableSystem,
         AndroidPackage::getEmergencyInstaller,
         AndroidPackage::isAllowCrossUidActivitySwitchFromBelow,
+        AndroidPackage::getIntentMatchingFlags,
     )
 
     override fun extraParams() = listOf(
@@ -613,6 +627,9 @@
         .setSplitClassLoaderName(1, "testSplitClassLoaderNameOne")
         .addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1"), true)
         .addUsesStaticLibrary("testStatic", 3L, arrayOf("testCertDigest2"))
+        .addFeatureFlag("testFlag1", null)
+        .addFeatureFlag("testFlag2", true)
+        .addFeatureFlag("testFlag3", false)
 
     override fun finalizeObject(parcelable: Parcelable) {
         (parcelable as PackageImpl).hideAsParsed().hideAsFinal()
@@ -673,6 +690,12 @@
                 .containsExactly("testCertDigest2")
 
         expect.that(after.storageUuid).isEqualTo(TEST_UUID)
+
+        expect.that(after.featureFlagState).containsExactlyEntriesIn(mapOf(
+            "testFlag1" to null,
+            "testFlag2" to true,
+            "testFlag3" to false,
+        ))
     }
 
     private fun testKey() = KeyPairGenerator.getInstance("RSA")
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index 349b831..8782b1a 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -55,7 +55,8 @@
         ParsedActivity::getUiOptions,
         ParsedActivity::isSupportsSizeChanges,
         ParsedActivity::getRequiredDisplayCategory,
-        ParsedActivity::getRequireContentUriPermissionFromCaller
+        ParsedActivity::getRequireContentUriPermissionFromCaller,
+        ParsedActivity::getIntentMatchingFlags,
     )
 
     override fun mainComponentSubclassExtraParams() = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
index 1e84470..3e34979 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
@@ -38,6 +38,7 @@
         ParsedProvider::isForceUriPermissions,
         ParsedProvider::isMultiProcess,
         ParsedProvider::getInitOrder,
+        ParsedProvider::getIntentMatchingFlags,
     )
 
     override fun mainComponentSubclassExtraParams() = listOf(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
index 79d5a4f..694db47 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
@@ -30,5 +30,6 @@
     override val mainComponentSubclassBaseParams = listOf(
         ParsedService::getForegroundServiceType,
         ParsedService::getPermission,
+        ParsedService::getIntentMatchingFlags,
     )
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
index de53266..fc4cc25 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
@@ -63,11 +63,12 @@
                 DisplayManagerInternal.DisplayPowerRequest.class);
 
         displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+        displayPowerRequest.policyReason = Display.STATE_REASON_KEY;
         Pair<Integer, Integer> stateAndReason =
                 mDisplayStateController.updateDisplayState(
                         displayPowerRequest, DISPLAY_ENABLED, !DISPLAY_IN_TRANSITION);
         assertTrue(Display.STATE_OFF == stateAndReason.first);
-        assertTrue(Display.STATE_REASON_DEFAULT_POLICY == stateAndReason.second);
+        assertTrue(Display.STATE_REASON_KEY == stateAndReason.second);
         verify(mDisplayPowerProximityStateController).updateProximityState(displayPowerRequest,
                 Display.STATE_OFF);
         assertEquals(true, mDisplayStateController.shouldPerformScreenOffTransition());
@@ -105,11 +106,12 @@
                 DisplayManagerInternal.DisplayPowerRequest.class);
 
         displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        displayPowerRequest.policyReason = Display.STATE_REASON_KEY;
         Pair<Integer, Integer> stateAndReason =
                 mDisplayStateController.updateDisplayState(
                         displayPowerRequest, !DISPLAY_ENABLED, !DISPLAY_IN_TRANSITION);
         assertTrue(Display.STATE_OFF == stateAndReason.first);
-        assertTrue(Display.STATE_REASON_DEFAULT_POLICY == stateAndReason.second);
+        assertTrue(Display.STATE_REASON_KEY == stateAndReason.second);
         verify(mDisplayPowerProximityStateController).updateProximityState(displayPowerRequest,
                 Display.STATE_ON);
         assertEquals(false, mDisplayStateController.shouldPerformScreenOffTransition());
@@ -123,11 +125,12 @@
                 DisplayManagerInternal.DisplayPowerRequest.class);
 
         displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        displayPowerRequest.policyReason = Display.STATE_REASON_MOTION;
         Pair<Integer, Integer> stateAndReason =
                 mDisplayStateController.updateDisplayState(
                         displayPowerRequest, DISPLAY_ENABLED, DISPLAY_IN_TRANSITION);
         assertTrue(Display.STATE_OFF == stateAndReason.first);
-        assertTrue(Display.STATE_REASON_DEFAULT_POLICY == stateAndReason.second);
+        assertTrue(Display.STATE_REASON_MOTION == stateAndReason.second);
         verify(mDisplayPowerProximityStateController).updateProximityState(displayPowerRequest,
                 Display.STATE_ON);
         assertEquals(false, mDisplayStateController.shouldPerformScreenOffTransition());
@@ -141,6 +144,7 @@
                 DisplayManagerInternal.DisplayPowerRequest.class);
 
         displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        displayPowerRequest.policyReason = Display.STATE_REASON_DEFAULT_POLICY;
         Pair<Integer, Integer> stateAndReason =
                 mDisplayStateController.updateDisplayState(
                         displayPowerRequest, DISPLAY_ENABLED, !DISPLAY_IN_TRANSITION);
@@ -156,6 +160,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 new DisplayManagerInternal.DisplayPowerRequest();
         displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+        displayPowerRequest.policyReason = Display.STATE_REASON_DRAW_WAKE_LOCK;
         mDisplayStateController.overrideDozeScreenState(
                 Display.STATE_DOZE_SUSPEND, Display.STATE_REASON_OFFLOAD);
 
@@ -172,8 +177,9 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 new DisplayManagerInternal.DisplayPowerRequest();
         displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+        displayPowerRequest.policyReason = Display.STATE_REASON_DEFAULT_POLICY;
         mDisplayStateController.overrideDozeScreenState(
-                Display.STATE_DOZE_SUSPEND, Display.STATE_REASON_DEFAULT_POLICY);
+                Display.STATE_DOZE_SUSPEND, Display.STATE_REASON_DRAW_WAKE_LOCK);
 
         Pair<Integer, Integer> stateAndReason =
                 mDisplayStateController.updateDisplayState(
@@ -183,6 +189,53 @@
         assertTrue(Display.STATE_REASON_DEFAULT_POLICY == stateAndReason.second);
     }
 
+    @Test
+    public void policyOff_usespolicyReasonFromRequest() {
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+        displayPowerRequest.policyReason = Display.STATE_REASON_KEY;
+
+        Pair<Integer, Integer> stateAndReason =
+                mDisplayStateController.updateDisplayState(
+                        displayPowerRequest, DISPLAY_ENABLED, !DISPLAY_IN_TRANSITION);
+
+        assertTrue(Display.STATE_OFF == stateAndReason.first);
+        assertTrue(Display.STATE_REASON_KEY == stateAndReason.second);
+    }
+
+    @Test
+    public void policyBright_usespolicyReasonFromRequest() {
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+        displayPowerRequest.policyReason = Display.STATE_REASON_DREAM_MANAGER;
+
+        Pair<Integer, Integer> stateAndReason =
+                mDisplayStateController.updateDisplayState(
+                        displayPowerRequest, DISPLAY_ENABLED, !DISPLAY_IN_TRANSITION);
+
+        assertTrue(Display.STATE_ON == stateAndReason.first);
+        assertTrue(Display.STATE_REASON_DREAM_MANAGER == stateAndReason.second);
+    }
+
+    @Test
+    public void policyRequestHasDozeScreenState_usesPolicyDozeScreenStateReason() {
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+        displayPowerRequest.policyReason = Display.STATE_REASON_MOTION;
+        displayPowerRequest.dozeScreenState = Display.STATE_ON;
+        displayPowerRequest.dozeScreenStateReason = Display.STATE_REASON_OFFLOAD;
+
+        Pair<Integer, Integer> stateAndReason =
+                mDisplayStateController.updateDisplayState(
+                        displayPowerRequest, DISPLAY_ENABLED, !DISPLAY_IN_TRANSITION);
+
+        assertTrue(Display.STATE_ON == stateAndReason.first);
+        assertTrue(Display.STATE_REASON_OFFLOAD == stateAndReason.second);
+    }
+
     private void validDisplayState(int policy, int displayState, boolean isEnabled,
             boolean isInTransition) {
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
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/OWNERS b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
index 4fac647..809b7bb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
@@ -2,3 +2,4 @@
 
 per-file ApplicationStartInfoTest.java = yforta@google.com, carmenjackson@google.com, jji@google.com
 per-file CachedAppOptimizerTest.java = file:/PERFORMANCE_OWNERS
+per-file BaseBroadcastQueueTest.java = file:/BROADCASTS_OWNERS
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/JobServiceContextTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java
new file mode 100644
index 0000000..8c66fd0
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobServiceContextTest.java
@@ -0,0 +1,270 @@
+/*
+ * 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.job;
+
+import static android.app.job.Flags.FLAG_HANDLE_ABANDONED_JOBS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.app.AppGlobals;
+import android.app.job.JobParameters;
+import android.content.Context;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.server.job.JobServiceContext.JobCallback;
+import com.android.server.job.controllers.JobStatus;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+public class JobServiceContextTest {
+    private static final String TAG = JobServiceContextTest.class.getSimpleName();
+    @ClassRule
+    public static final SetFlagsRule.ClassRule mSetFlagsClassRule = new SetFlagsRule.ClassRule();
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = mSetFlagsClassRule.createSetFlagsRule();
+    @Mock
+    private JobSchedulerService mMockJobSchedulerService;
+    @Mock
+    private JobConcurrencyManager mMockConcurrencyManager;
+    @Mock
+    private JobNotificationCoordinator mMockNotificationCoordinator;
+    @Mock
+    private IBatteryStats.Stub mMockBatteryStats;
+    @Mock
+    private JobPackageTracker mMockJobPackageTracker;
+    @Mock
+    private Looper mMockLooper;
+    @Mock
+    private Context mMockContext;
+    @Mock
+    private JobStatus mMockJobStatus;
+    @Mock
+    private JobParameters mMockJobParameters;
+    @Mock
+    private JobCallback mMockJobCallback;
+    private MockitoSession mMockingSession;
+    private JobServiceContext mJobServiceContext;
+    private Object mLock;
+
+    @Before
+    public void setUp() throws Exception {
+        mMockingSession =
+                mockitoSession()
+                        .initMocks(this)
+                        .mockStatic(AppGlobals.class)
+                        .strictness(Strictness.LENIENT)
+                        .startMocking();
+        JobSchedulerService.sElapsedRealtimeClock =
+                Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+        doReturn(mock(PowerManager.class)).when(mMockContext).getSystemService(PowerManager.class);
+        doReturn(mMockContext).when(mMockJobSchedulerService).getContext();
+        mLock = new Object();
+        doReturn(mLock).when(mMockJobSchedulerService).getLock();
+        mJobServiceContext =
+                new JobServiceContext(
+                        mMockJobSchedulerService,
+                        mMockConcurrencyManager,
+                        mMockNotificationCoordinator,
+                        mMockBatteryStats,
+                        mMockJobPackageTracker,
+                        mMockLooper);
+        spyOn(mJobServiceContext);
+        mJobServiceContext.setJobParamsLockedForTest(mMockJobParameters);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private Clock getAdvancedClock(Clock clock, long incrementMs) {
+        return Clock.offset(clock, Duration.ofMillis(incrementMs));
+    }
+
+    private void advanceElapsedClock(long incrementMs) {
+        JobSchedulerService.sElapsedRealtimeClock =
+                getAdvancedClock(JobSchedulerService.sElapsedRealtimeClock, incrementMs);
+    }
+
+    /**
+     * Test that Abandoned jobs that are timed out are stopped with the correct stop reason
+     */
+    @Test
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    public void testJobServiceContext_TimeoutAbandonedJob() {
+        mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        doNothing().when(mJobServiceContext).sendStopMessageLocked(captor.capture());
+
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS); // 30 minutes
+        mJobServiceContext.setPendingStopReasonLockedForTest(JobParameters.STOP_REASON_UNDEFINED);
+
+        mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
+        doReturn(true).when(mMockJobStatus).isAbandoned();
+        mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
+
+        mJobServiceContext.handleOpTimeoutLocked();
+
+        String stopMessage = captor.getValue();
+        assertEquals("timeout while executing and maybe abandoned", stopMessage);
+        verify(mMockJobParameters)
+                .setStopReason(
+                        JobParameters.STOP_REASON_TIMEOUT_ABANDONED,
+                        JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED,
+                        "client timed out and maybe abandoned");
+    }
+
+    /**
+     * Test that non-abandoned jobs that are timed out are stopped with the correct stop reason
+     */
+    @Test
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    public void testJobServiceContext_TimeoutNoAbandonedJob() {
+        mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        doNothing().when(mJobServiceContext).sendStopMessageLocked(captor.capture());
+
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS); // 30 minutes
+        mJobServiceContext.setPendingStopReasonLockedForTest(JobParameters.STOP_REASON_UNDEFINED);
+
+        mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
+        doReturn(false).when(mMockJobStatus).isAbandoned();
+        mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
+
+        mJobServiceContext.handleOpTimeoutLocked();
+
+        String stopMessage = captor.getValue();
+        assertEquals("timeout while executing", stopMessage);
+        verify(mMockJobParameters)
+                .setStopReason(
+                        JobParameters.STOP_REASON_TIMEOUT,
+                        JobParameters.INTERNAL_STOP_REASON_TIMEOUT,
+                        "client timed out");
+    }
+
+    /**
+     * Test that abandoned jobs that are timed out while the flag is disabled
+     * are stopped with the correct stop reason
+     */
+    @Test
+    @DisableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    public void testJobServiceContext_TimeoutAbandonedJob_flagHandleAbandonedJobsDisabled() {
+        mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        doNothing().when(mJobServiceContext).sendStopMessageLocked(captor.capture());
+
+        advanceElapsedClock(30 * MINUTE_IN_MILLIS); // 30 minutes
+        mJobServiceContext.setPendingStopReasonLockedForTest(JobParameters.STOP_REASON_UNDEFINED);
+
+        mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
+        doReturn(true).when(mMockJobStatus).isAbandoned();
+        mJobServiceContext.mVerb = JobServiceContext.VERB_EXECUTING;
+
+        mJobServiceContext.handleOpTimeoutLocked();
+
+        String stopMessage = captor.getValue();
+        assertEquals("timeout while executing", stopMessage);
+        verify(mMockJobParameters)
+                .setStopReason(
+                        JobParameters.STOP_REASON_TIMEOUT,
+                        JobParameters.INTERNAL_STOP_REASON_TIMEOUT,
+                        "client timed out");
+    }
+
+    /**
+     * Test that the JobStatus is marked as abandoned when the JobServiceContext
+     * receives a MSG_HANDLE_ABANDONED_JOB message
+     */
+    @Test
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    public void testJobServiceContext_HandleAbandonedJob() {
+        final int jobId = 123;
+        mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
+        mJobServiceContext.setRunningCallbackLockedForTest(mMockJobCallback);
+        doReturn(jobId).when(mMockJobStatus).getJobId();
+
+        mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId);
+
+        verify(mMockJobStatus).setAbandoned(true);
+    }
+
+    /**
+     * Test that the JobStatus is not marked as abandoned when the
+     * JobServiceContext receives a MSG_HANDLE_ABANDONED_JOB message and the
+     * JobServiceContext is not running a job
+     */
+    @Test
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    public void testJobServiceContext_HandleAbandonedJob_notRunningJob() {
+        final int jobId = 123;
+        mJobServiceContext.setRunningJobLockedForTest(null);
+        mJobServiceContext.setRunningCallbackLockedForTest(mMockJobCallback);
+
+        mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId);
+
+        verify(mMockJobStatus, never()).setAbandoned(true);
+    }
+
+    /**
+     * Test that the JobStatus is not marked as abandoned when the
+     * JobServiceContext receives a MSG_HANDLE_ABANDONED_JOB message and the
+     * JobServiceContext is running a job with a different jobId
+     */
+    @Test
+    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
+    public void testJobServiceContext_HandleAbandonedJob_differentJobId() {
+        final int jobId = 123;
+        final int differentJobId = 456;
+        mJobServiceContext.setRunningJobLockedForTest(mMockJobStatus);
+        mJobServiceContext.setRunningCallbackLockedForTest(mMockJobCallback);
+        doReturn(differentJobId).when(mMockJobStatus).getJobId();
+
+        mJobServiceContext.doHandleAbandonedJob(mMockJobCallback, jobId);
+
+        verify(mMockJobStatus, never()).setAbandoned(true);
+    }
+
+}
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/BackgroundInstallControlCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java
index 574f369..ae404cf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundInstallControlCallbackHelperTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY;
 import static com.android.server.pm.BackgroundInstallControlCallbackHelper.FLAGGED_USER_ID_KEY;
+import static com.android.server.pm.BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.after;
@@ -84,12 +85,18 @@
         int testUserId = 1;
         mCallbackHelper.registerBackgroundInstallCallback(mCallback);
 
-        mCallbackHelper.notifyAllCallbacks(testUserId, testPackageName);
+        mCallbackHelper.notifyAllCallbacks(
+                testUserId,
+                testPackageName,
+                BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL);
 
         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
         verify(mCallback, after(1000).times(1)).sendResult(bundleCaptor.capture());
         Bundle receivedBundle = bundleCaptor.getValue();
         assertEquals(testPackageName, receivedBundle.getString(FLAGGED_PACKAGE_NAME_KEY));
         assertEquals(testUserId, receivedBundle.getInt(FLAGGED_USER_ID_KEY));
+        assertEquals(
+                BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL,
+                receivedBundle.getInt(INSTALL_EVENT_TYPE_KEY));
     }
 }
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/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
index f1bf86f..b53f6fb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -16,11 +16,11 @@
 
 package com.android.server.wallpaper;
 
-import static android.app.WallpaperManager.LANDSCAPE;
+import static android.app.WallpaperManager.ORIENTATION_LANDSCAPE;
 import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
-import static android.app.WallpaperManager.PORTRAIT;
-import static android.app.WallpaperManager.SQUARE_LANDSCAPE;
-import static android.app.WallpaperManager.SQUARE_PORTRAIT;
+import static android.app.WallpaperManager.ORIENTATION_PORTRAIT;
+import static android.app.WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE;
+import static android.app.WallpaperManager.ORIENTATION_SQUARE_PORTRAIT;
 import static android.app.WallpaperManager.getOrientation;
 import static android.app.WallpaperManager.getRotatedOrientation;
 
@@ -406,15 +406,16 @@
         setUpWithDisplays(STANDARD_DISPLAY);
         Point bitmapSize = new Point(800, 1000);
         SparseArray<Rect> suggestedCrops = new SparseArray<>();
-        suggestedCrops.put(PORTRAIT, new Rect(0, 0, 400, 800));
-        for (int otherOrientation: List.of(LANDSCAPE, SQUARE_LANDSCAPE, SQUARE_PORTRAIT)) {
+        suggestedCrops.put(ORIENTATION_PORTRAIT, new Rect(0, 0, 400, 800));
+        for (int otherOrientation: List.of(ORIENTATION_LANDSCAPE, ORIENTATION_SQUARE_LANDSCAPE,
+                ORIENTATION_SQUARE_PORTRAIT)) {
             suggestedCrops.put(otherOrientation, new Rect(0, 0, 10, 10));
         }
 
         for (boolean rtl : List.of(false, true)) {
             assertThat(mWallpaperCropper.getCrop(
                     new Point(300, 800), bitmapSize, suggestedCrops, rtl))
-                    .isEqualTo(suggestedCrops.get(PORTRAIT));
+                    .isEqualTo(suggestedCrops.get(ORIENTATION_PORTRAIT));
             assertThat(mWallpaperCropper.getCrop(
                     new Point(500, 800), bitmapSize, suggestedCrops, rtl))
                     .isEqualTo(new Rect(0, 0, 500, 800));
@@ -440,8 +441,8 @@
         Point landscape = new Point(PORTRAIT_ONE.y, PORTRAIT_ONE.x);
         Point squarePortrait = SQUARE_PORTRAIT_ONE;
         Point squareLandscape = new Point(SQUARE_PORTRAIT_ONE.y, SQUARE_PORTRAIT_ONE.y);
-        suggestedCrops.put(PORTRAIT, centerOf(bitmapRect, portrait));
-        suggestedCrops.put(SQUARE_LANDSCAPE, centerOf(bitmapRect, squareLandscape));
+        suggestedCrops.put(ORIENTATION_PORTRAIT, centerOf(bitmapRect, portrait));
+        suggestedCrops.put(ORIENTATION_SQUARE_LANDSCAPE, centerOf(bitmapRect, squareLandscape));
         for (boolean rtl : List.of(false, true)) {
             assertThat(mWallpaperCropper.getCrop(
                     landscape, bitmapSize, suggestedCrops, rtl))
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..db323f1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -40,13 +40,13 @@
 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;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
@@ -55,6 +55,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 +66,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 +428,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 +439,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 +467,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());
@@ -516,7 +536,8 @@
         doReturn(wallpaper).when(mService).getWallpaperSafeLocked(wallpaper.userId, FLAG_SYSTEM);
         doNothing().when(mService).switchWallpaper(any(), any());
         doReturn(true).when(mService)
-                .bindWallpaperComponentLocked(any(), anyBoolean(), anyBoolean(), any(), any());
+                .bindWallpaperComponentLocked(isA(ComponentName.class), anyBoolean(), anyBoolean(),
+                        any(), any());
         doNothing().when(mService).saveSettingsLocked(wallpaper.userId);
         spyOn(mService.mWallpaperCropper);
         doNothing().when(mService.mWallpaperCropper).generateCrop(wallpaper);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
index 473c8c5..648da65 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
@@ -27,10 +27,13 @@
 import static android.os.PowerManager.GO_TO_SLEEP_REASON_TIMEOUT;
 import static android.os.PowerManager.WAKE_REASON_GESTURE;
 import static android.os.PowerManager.WAKE_REASON_PLUGGED_IN;
+import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
 import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
 import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
 import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
 import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
+import static android.view.Display.STATE_REASON_DEFAULT_POLICY;
+import static android.view.Display.STATE_REASON_MOTION;
 
 import static com.android.server.power.PowerManagerService.USER_ACTIVITY_SCREEN_BRIGHT;
 import static com.android.server.power.PowerManagerService.WAKE_LOCK_DOZE;
@@ -44,6 +47,7 @@
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.hardware.display.DisplayManagerInternal;
 import android.os.PowerManager;
@@ -53,6 +57,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.util.LatencyTracker;
+import com.android.server.power.feature.PowerManagerFlags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -79,18 +84,23 @@
     private static final float BRIGHTNESS = 0.99f;
     private static final float BRIGHTNESS_DOZE = 0.5f;
 
+    private static final LatencyTracker LATENCY_TRACKER = LatencyTracker.getInstance(
+            InstrumentationRegistry.getInstrumentation().getContext());
+
     private PowerGroup mPowerGroup;
     @Mock private PowerGroup.PowerGroupListener mWakefulnessCallbackMock;
     @Mock private Notifier mNotifier;
     @Mock private DisplayManagerInternal mDisplayManagerInternal;
+    @Mock private PowerManagerFlags mFeatureFlags;
 
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        when(mFeatureFlags.isPolicyReasonInDisplayPowerRequestEnabled()).thenReturn(true);
         mPowerGroup = new PowerGroup(GROUP_ID, mWakefulnessCallbackMock, mNotifier,
                 mDisplayManagerInternal, WAKEFULNESS_AWAKE, /* ready= */ true,
-                /* supportsSandman= */ true, TIMESTAMP_CREATE);
+                /* supportsSandman= */ true, TIMESTAMP_CREATE, mFeatureFlags);
     }
 
     @Test
@@ -101,10 +111,8 @@
                 eq(UID), /* opUid= */anyInt(), /* opPackageName= */ isNull(), /* details= */
                 isNull());
         String details = "wake PowerGroup1";
-        LatencyTracker latencyTracker = LatencyTracker.getInstance(
-                InstrumentationRegistry.getInstrumentation().getContext());
         mPowerGroup.wakeUpLocked(TIMESTAMP2, WAKE_REASON_PLUGGED_IN, details, UID,
-                /* opPackageName= */ null, /* opUid= */ 0, latencyTracker);
+                /* opPackageName= */ null, /* opUid= */ 0, LATENCY_TRACKER);
         verify(mWakefulnessCallbackMock).onWakefulnessChangedLocked(eq(GROUP_ID),
                 eq(WAKEFULNESS_AWAKE), eq(TIMESTAMP2), eq(WAKE_REASON_PLUGGED_IN), eq(UID),
                 /* opUid= */ anyInt(), /* opPackageName= */ isNull(), eq(details));
@@ -247,6 +255,10 @@
 
     @Test
     public void testUpdateWhileAwake_UpdatesDisplayPowerRequest() {
+        mPowerGroup.dozeLocked(TIMESTAMP1, UID, GO_TO_SLEEP_REASON_APPLICATION);
+        mPowerGroup.wakeUpLocked(TIMESTAMP2, WAKE_REASON_WAKE_MOTION, "details", UID,
+                /* opPackageName= */ null, /* opUid= */ 0, LATENCY_TRACKER);
+
         final boolean batterySaverEnabled = true;
         float brightnessFactor = 0.7f;
         PowerSaveState powerSaveState = new PowerSaveState.Builder()
@@ -274,6 +286,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DIM);
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_MOTION);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.screenBrightnessOverrideTag.toString()).isEqualTo(tag);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(false);
@@ -288,6 +301,55 @@
     }
 
     @Test
+    public void testWakefulnessReasonInDisplayPowerRequestDisabled_wakefulnessReasonNotPopulated() {
+        when(mFeatureFlags.isPolicyReasonInDisplayPowerRequestEnabled()).thenReturn(false);
+        mPowerGroup.dozeLocked(TIMESTAMP1, UID, GO_TO_SLEEP_REASON_APPLICATION);
+        mPowerGroup.wakeUpLocked(TIMESTAMP2, WAKE_REASON_WAKE_MOTION, "details", UID,
+                /* opPackageName= */ null, /* opUid= */ 0, LATENCY_TRACKER);
+
+        mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ "my/tag",
+                /* useProximitySensor= */ false,
+                /* boostScreenBrightness= */ false,
+                /* dozeScreenStateOverride= */ Display.STATE_ON,
+                /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
+                /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
+                /* overrideDrawWakeLock= */ false,
+                new PowerSaveState.Builder().build(),
+                /* quiescent= */ false,
+                /* dozeAfterScreenOff= */ false,
+                /* bootCompleted= */ true,
+                /* screenBrightnessBoostInProgress= */ false,
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+                mPowerGroup.mDisplayPowerRequest;
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
+
+        mPowerGroup.wakeUpLocked(TIMESTAMP2, WAKE_REASON_PLUGGED_IN, "details", UID,
+                /* opPackageName= */ null, /* opUid= */ 0, LATENCY_TRACKER);
+        mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ "my/tag",
+                /* useProximitySensor= */ false,
+                /* boostScreenBrightness= */ false,
+                /* dozeScreenStateOverride= */ Display.STATE_ON,
+                /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
+                /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
+                /* overrideDrawWakeLock= */ false,
+                new PowerSaveState.Builder().build(),
+                /* quiescent= */ false,
+                /* dozeAfterScreenOff= */ false,
+                /* bootCompleted= */ true,
+                /* screenBrightnessBoostInProgress= */ false,
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
+        displayPowerRequest = mPowerGroup.mDisplayPowerRequest;
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
+    }
+
+    @Test
     public void testUpdateWhileDozing_UpdatesDisplayPowerRequest() {
         final boolean useNormalBrightnessForDoze = false;
         final boolean batterySaverEnabled = false;
@@ -319,6 +381,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DOZE);
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
@@ -363,6 +426,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DOZE);
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
@@ -405,6 +469,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF);
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
@@ -419,6 +484,10 @@
 
     @Test
     public void testUpdateQuiescent() {
+        mPowerGroup.dozeLocked(TIMESTAMP1, UID, GO_TO_SLEEP_REASON_APPLICATION);
+        mPowerGroup.wakeUpLocked(TIMESTAMP2, WAKE_REASON_WAKE_MOTION, "details", UID,
+                /* opPackageName= */ null, /* opUid= */ 0, LATENCY_TRACKER);
+
         final boolean batterySaverEnabled = false;
         float brightnessFactor = 0.3f;
         PowerSaveState powerSaveState = new PowerSaveState.Builder()
@@ -446,6 +515,11 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF);
+        // Note how the reason is STATE_REASON_DEFAULT_POLICY, instead of STATE_REASON_MOTION.
+        // This is because - although there was a wake up request from a motion, the quiescent state
+        // preceded and forced the policy to be OFF, so we ignore the reason associated with the
+        // wake up request.
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
@@ -487,6 +561,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF);
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
@@ -529,6 +604,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
@@ -569,6 +645,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
@@ -610,6 +687,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
@@ -650,6 +728,7 @@
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
+        assertThat(displayPowerRequest.policyReason).isEqualTo(STATE_REASON_DEFAULT_POLICY);
         assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
         assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
         assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index e9e21de..b10200d 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -70,7 +70,6 @@
 import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.hardware.power.Boost;
 import android.hardware.power.Mode;
 import android.os.BatteryManager;
@@ -1894,15 +1893,6 @@
     }
 
     @Test
-    public void testBoot_DesiredScreenPolicyShouldBeBright() {
-        createService();
-        startSystem();
-
-        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
-                DisplayPowerRequest.POLICY_BRIGHT);
-    }
-
-    @Test
     public void testQuiescentBoot_ShouldBeAsleep() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
@@ -1914,40 +1904,6 @@
     }
 
     @Test
-    public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() {
-        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
-        createService();
-        startSystem();
-        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
-                DisplayPowerRequest.POLICY_OFF);
-    }
-
-    @Test
-    public void testQuiescentBoot_WakeUp_DesiredScreenPolicyShouldBeBright() {
-        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
-        createService();
-        startSystem();
-        forceAwake();
-        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
-                DisplayPowerRequest.POLICY_BRIGHT);
-    }
-
-    @Test
-    public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted() {
-        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
-        createService();
-        startSystem();
-
-        mService.getBinderServiceInstance().wakeUp(mClock.now(),
-                PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
-
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
-        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
-        assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo(
-                DisplayPowerRequest.POLICY_BRIGHT);
-    }
-
-    @Test
     public void testIsAmbientDisplayAvailable_available() {
         createService();
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
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 feb00e7..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
@@ -36,13 +36,13 @@
 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.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -64,7 +64,6 @@
     private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
             IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
 
-    @Mock
     private Context mContext;
     private BackupTransportConnection mBackupTransportConnection;
     private DataAggregator mDataAggregator;
@@ -77,7 +76,7 @@
     @SuppressLint("VisibleForTests")
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
 
         mTestLooper = new TestLooper();
         mLooper = mTestLooper.getLooper();
@@ -491,7 +490,7 @@
 
         @Override
         public DataAggregator getDataAggregator(ForensicService forensicService) {
-            mDataAggregator = spy(new DataAggregator(forensicService));
+            mDataAggregator = spy(new DataAggregator(mContext, forensicService));
             return mDataAggregator;
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 7481fc8..2edde9b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -1615,7 +1615,8 @@
                 List.of(tile)
         );
 
-        assertThat(mA11yms.getCurrentUserState().getA11yQsTargets()).doesNotContain(tile);
+        assertThat(mA11yms.getCurrentUserState()
+                .getShortcutTargetsLocked(QUICK_SETTINGS)).doesNotContain(tile.flattenToString());
     }
 
     @Test
@@ -1636,7 +1637,7 @@
                 List.of(tile)
         );
 
-        assertThat(mA11yms.getCurrentUserState().getA11yQsTargets())
+        assertThat(mA11yms.getCurrentUserState().getShortcutTargetsLocked(QUICK_SETTINGS))
                 .contains(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString());
     }
 
@@ -1656,7 +1657,7 @@
         );
 
         assertThat(
-                mA11yms.getCurrentUserState().getA11yQsTargets()
+                mA11yms.getCurrentUserState().getShortcutTargetsLocked(QUICK_SETTINGS)
         ).containsExactlyElementsIn(List.of(
                 AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString(),
                 AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME.flattenToString())
@@ -1676,7 +1677,7 @@
         );
 
         assertThat(
-                mA11yms.getCurrentUserState().getA11yQsTargets()
+                mA11yms.getCurrentUserState().getShortcutTargetsLocked(QUICK_SETTINGS)
         ).doesNotContain(
                 AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME.flattenToString());
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index 627b5e3..8c35925 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -29,6 +29,7 @@
 import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
@@ -72,6 +73,7 @@
 import com.android.internal.accessibility.AccessibilityShortcutController;
 import com.android.internal.accessibility.common.ShortcutConstants;
 import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
+import com.android.internal.accessibility.util.ShortcutUtils;
 import com.android.internal.util.test.FakeSettingsProvider;
 
 import org.junit.After;
@@ -454,17 +456,7 @@
 
         mUserState.updateShortcutTargetsLocked(newTargets, QUICK_SETTINGS);
 
-        assertThat(mUserState.getA11yQsTargets()).isEqualTo(newTargets);
-    }
-
-    @Test
-    public void getA11yQsTargets_returnsCopiedData() {
-        updateShortcutTargetsLocked_quickSettings_valueUpdated();
-
-        Set<String> targets = mUserState.getA11yQsTargets();
-        targets.clear();
-
-        assertThat(mUserState.getA11yQsTargets()).isNotEmpty();
+        assertThat(mUserState.getShortcutTargetsLocked(QUICK_SETTINGS)).isEqualTo(newTargets);
     }
 
     @Test
@@ -539,6 +531,31 @@
         assertThat(mUserState.isShortcutMagnificationEnabledLocked()).isFalse();
     }
 
+    @Test
+    public void getShortcutTargetsLocked_returnsCorrectTargets() {
+        for (int shortcutType : ShortcutConstants.USER_SHORTCUT_TYPES) {
+            if (((TRIPLETAP | TWOFINGER_DOUBLETAP) & shortcutType) == shortcutType) {
+                continue;
+            }
+            Set<String> expectedSet = Set.of(ShortcutUtils.convertToKey(shortcutType));
+            mUserState.updateShortcutTargetsLocked(expectedSet, shortcutType);
+
+            assertThat(mUserState.getShortcutTargetsLocked(shortcutType))
+                    .containsExactlyElementsIn(expectedSet);
+        }
+    }
+
+    @Test
+    public void getShortcutTargetsLocked_returnsCopiedData() {
+        Set<String> set = Set.of("FOO", "BAR");
+        mUserState.updateShortcutTargetsLocked(set, SOFTWARE);
+
+        Set<String> targets = mUserState.getShortcutTargetsLocked(ALL);
+        targets.clear();
+
+        assertThat(mUserState.getShortcutTargetsLocked(ALL)).isNotEmpty();
+    }
+
     private int getSecureIntForUser(String key, int userId) {
         return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId);
     }
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/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 2f7b8d2..4ef37b9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -69,6 +69,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 import android.security.KeyStoreAuthorization;
 import android.testing.TestableContext;
@@ -119,6 +120,7 @@
     @Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver;
     @Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
     @Mock private BiometricCameraManager mBiometricCameraManager;
+    @Mock private UserManager mUserManager;
     @Mock private BiometricManager mBiometricManager;
 
     private Random mRandom;
@@ -846,7 +848,8 @@
                 TEST_PACKAGE,
                 checkDevicePolicyManager,
                 mContext,
-                mBiometricCameraManager);
+                mBiometricCameraManager,
+                mUserManager);
     }
 
     private AuthSession createAuthSession(List<BiometricSensor> sensors,
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 85e45f4..cf628ad 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -39,6 +39,7 @@
 import android.hardware.biometrics.IBiometricAuthenticator;
 import android.hardware.biometrics.PromptInfo;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
@@ -64,6 +65,8 @@
     public final CheckFlagsRule mCheckFlagsRule =
             DeviceFlagsValueProvider.createCheckFlagsRule();
 
+    private static final int USER_ID = 0;
+    private static final int OWNER_ID = 10;
     private static final int SENSOR_ID_FINGERPRINT = 0;
     private static final int SENSOR_ID_FACE = 1;
     private static final String TEST_PACKAGE_NAME = "PreAuthInfoTestPackage";
@@ -84,6 +87,8 @@
     BiometricService.SettingObserver mSettingObserver;
     @Mock
     BiometricCameraManager mBiometricCameraManager;
+    @Mock
+    UserManager mUserManager;
 
     @Before
     public void setup() throws RemoteException {
@@ -118,9 +123,9 @@
         promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
         promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
         PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
-                mSettingObserver, List.of(sensor),
-                0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
-                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+                mSettingObserver, List.of(sensor), USER_ID, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
 
         assertThat(preAuthInfo.eligibleSensors).isEmpty();
     }
@@ -134,9 +139,9 @@
         promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
         promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
         PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
-                mSettingObserver, List.of(sensor),
-                0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
-                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+                mSettingObserver, List.of(sensor), USER_ID, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
 
         assertThat(preAuthInfo.eligibleSensors).hasSize(1);
     }
@@ -151,9 +156,9 @@
         promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
         promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
         PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
-                mSettingObserver, List.of(sensor),
-                0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
-                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+                mSettingObserver, List.of(sensor), USER_ID, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
 
         assertThat(preAuthInfo.eligibleSensors).hasSize(0);
     }
@@ -171,9 +176,9 @@
         promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
         promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
         PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
-                mSettingObserver, List.of(faceSensor, fingerprintSensor),
-                0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
-                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+                mSettingObserver, List.of(faceSensor, fingerprintSensor), USER_ID,
+                promptInfo, TEST_PACKAGE_NAME, false /* checkDevicePolicyManager */, mContext,
+                mBiometricCameraManager, mUserManager);
 
         assertThat(preAuthInfo.eligibleSensors).hasSize(0);
         assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(
@@ -191,9 +196,9 @@
         promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
         promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
         PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
-                mSettingObserver, List.of(faceSensor, fingerprintSensor),
-                0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
-                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+                mSettingObserver, List.of(faceSensor, fingerprintSensor), USER_ID,
+                promptInfo, TEST_PACKAGE_NAME, false /* checkDevicePolicyManager */, mContext,
+                mBiometricCameraManager, mUserManager);
 
         assertThat(preAuthInfo.eligibleSensors).hasSize(1);
         assertThat(preAuthInfo.eligibleSensors.get(0).modality).isEqualTo(TYPE_FINGERPRINT);
@@ -209,8 +214,9 @@
         final PromptInfo promptInfo = new PromptInfo();
         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);
+                mSettingObserver, List.of(sensor), USER_ID, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
 
         assertThat(preAuthInfo.eligibleSensors).hasSize(1);
     }
@@ -224,8 +230,9 @@
         final PromptInfo promptInfo = new PromptInfo();
         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);
+                mSettingObserver, List.of(), USER_ID, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
 
         assertThat(preAuthInfo.eligibleSensors).hasSize(0);
     }
@@ -241,8 +248,9 @@
         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,
-                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+                mSettingObserver, List.of(sensor), USER_ID, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
 
         assertThat(preAuthInfo.eligibleSensors).hasSize(1);
     }
@@ -257,8 +265,9 @@
         final PromptInfo promptInfo = new PromptInfo();
         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);
+                mSettingObserver, List.of(sensor), USER_ID, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
 
         assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(
                 BiometricManager.BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE);
@@ -279,9 +288,9 @@
         promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
         promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
         PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
-                mSettingObserver, List.of(faceSensor, fingerprintSensor),
-                0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
-                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+                mSettingObserver, List.of(faceSensor, fingerprintSensor), USER_ID,
+                promptInfo, TEST_PACKAGE_NAME, false /* checkDevicePolicyManager */, mContext,
+                mBiometricCameraManager, mUserManager);
 
         assertThat(preAuthInfo.eligibleSensors).hasSize(0);
         assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(
@@ -299,11 +308,30 @@
         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,
-                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+                mSettingObserver, List.of(sensor), USER_ID, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
         assertThat(promptInfo.getNegativeButtonText()).isEqualTo(TEST_PACKAGE_NAME);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_EFFECTIVE_USER_BP)
+    public void testCredentialOwnerIdAsUserId() throws Exception {
+        when(mUserManager.getCredentialOwnerProfile(USER_ID)).thenReturn(OWNER_ID);
+
+        final BiometricSensor sensor = getFaceSensor();
+        final PromptInfo promptInfo = new PromptInfo();
+        promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
+        promptInfo.setNegativeButtonText(TEST_PACKAGE_NAME);
+        final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+                mSettingObserver, List.of(sensor), USER_ID , promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager,
+                mUserManager);
+
+        assertThat(preAuthInfo.userId).isEqualTo(OWNER_ID);
+        assertThat(preAuthInfo.callingUserId).isEqualTo(USER_ID);
+    }
+
     private BiometricSensor getFingerprintSensor() {
         BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT,
                 TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG,
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 3360e1d..c741c6c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -37,6 +37,7 @@
 import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
@@ -2676,6 +2677,60 @@
     }
 
     @Test
+    public void onActiveSourceLost_oneTouchPlay_noStandbyAfterTimeout() {
+        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 activeSourceFromPlayback =
+                HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+                        mPlaybackPhysicalAddress);
+
+        assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+                .isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        // 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();
+        mHdmiControlService.oneTouchPlay(new IHdmiControlCallback() {
+            @Override
+            public void onComplete(int result) throws RemoteException {
+            }
+
+            @Override
+            public IBinder asBinder() {
+                return null;
+            }
+        });
+        mTestLooper.dispatchAll();
+
+        assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+        assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+                .isEqualTo(mPlaybackLogicalAddress);
+        assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+                .isEqualTo(mPlaybackPhysicalAddress);
+        mTestLooper.moveTimeForward(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(mPowerManager.isInteractive()).isTrue();
+    }
+
+    @Test
     public void handleRoutingChange_addressNotAllocated_removeActiveSourceAction() {
         long allocationDelay = TimeUnit.SECONDS.toMillis(60);
         mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
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/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/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
index 727b435..24bf6ca 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
@@ -16,6 +16,8 @@
 
 package com.android.server.security.advancedprotection;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
@@ -28,58 +30,274 @@
 import android.os.test.FakePermissionEnforcer;
 import android.os.test.TestLooper;
 import android.provider.Settings;
+import android.security.advancedprotection.AdvancedProtectionFeature;
+import android.security.advancedprotection.IAdvancedProtectionCallback;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.security.advancedprotection.features.AdvancedProtectionHook;
+import com.android.server.security.advancedprotection.features.AdvancedProtectionProvider;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.List;
+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;
+    AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature("test-id");
 
     @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, 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) {
+                    @NonNull
+                    @Override
+                    public AdvancedProtectionFeature getFeature() {
+                        return mFeature;
+                    }
+
+                    @Override
+                    public boolean isAvailable() {
+                        return true;
+                    }
+
+                    @Override
+                    public void onAdvancedProtectionChanged(boolean enabled) {
+                        callbackCaptor.set(enabled);
+                    }
+                };
+
+        mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+                mPermissionEnforcer, hook, null);
+        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) {
+                    @NonNull
+                    @Override
+                    public AdvancedProtectionFeature getFeature() {
+                        return mFeature;
+                    }
+
+                    @Override
+                    public boolean isAvailable() {
+                        return false;
+                    }
+
+                    @Override
+                    public void onAdvancedProtectionChanged(boolean enabled) {
+                        callbackCalledCaptor.set(true);
+                    }
+                };
+
+        mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+                mPermissionEnforcer, hook, null);
+
+        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) {
+                    @NonNull
+                    @Override
+                    public AdvancedProtectionFeature getFeature() {
+                        return mFeature;
+                    }
+
+                    @Override
+                    public boolean isAvailable() {
+                        return true;
+                    }
+
+                    @Override
+                    public void onAdvancedProtectionChanged(boolean enabled) {
+                        callbackCalledCaptor.set(true);
+                    }
+                };
+
+        mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+                mPermissionEnforcer, hook, null);
+        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 testGetFeatures() {
+        AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature("id-1");
+        AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature("id-2");
+        AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) {
+            @NonNull
+            @Override
+            public AdvancedProtectionFeature getFeature() {
+                return feature1;
+            }
+
+            @Override
+            public boolean isAvailable() {
+                return true;
+            }
+        };
+
+        AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
+            @Override
+            public List<AdvancedProtectionFeature> getFeatures() {
+                return List.of(feature2);
+            }
+        };
+
+        mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+                mPermissionEnforcer, hook, provider);
+        List<AdvancedProtectionFeature> features = mService.getAdvancedProtectionFeatures();
+        assertThat(features, containsInAnyOrder(feature1, feature2));
+    }
+
+    @Test
+    public void testGetFeatures_featureNotAvailable() {
+        AdvancedProtectionFeature feature1 = new AdvancedProtectionFeature("id-1");
+        AdvancedProtectionFeature feature2 = new AdvancedProtectionFeature("id-2");
+        AdvancedProtectionHook hook = new AdvancedProtectionHook(mContext, true) {
+            @NonNull
+            @Override
+            public AdvancedProtectionFeature getFeature() {
+                return feature1;
+            }
+
+            @Override
+            public boolean isAvailable() {
+                return false;
+            }
+        };
+
+        AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
+            @Override
+            public List<AdvancedProtectionFeature> getFeatures() {
+                return List.of(feature2);
+            }
+        };
+
+        mService = new AdvancedProtectionService(mContext, mStore, mLooper.getLooper(),
+                mPermissionEnforcer, hook, provider);
+        List<AdvancedProtectionFeature> features = mService.getAdvancedProtectionFeatures();
+        assertThat(features, containsInAnyOrder(feature2));
+    }
+
+
+    @Test
     public void testSetProtection_withoutPermission() {
         mPermissionEnforcer.revoke(Manifest.permission.SET_ADVANCED_PROTECTION_MODE);
         assertThrows(SecurityException.class, () -> mService.setAdvancedProtectionEnabled(true));
@@ -90,4 +308,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/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index a222ef0..5852af7 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.tv.tunerresourcemanager;
 
+import static android.media.tv.flags.Flags.FLAG_SET_RESOURCE_HOLDER_RETAIN;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.spy;
@@ -39,7 +41,9 @@
 import android.media.tv.tunerresourcemanager.TunerLnbRequest;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
 import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -47,6 +51,7 @@
 import com.google.common.truth.Correspondence;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -70,6 +75,8 @@
     private TunerResourceManagerService mTunerResourceManagerService;
     private boolean mIsForeground;
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private final class TunerClient extends IResourcesReclaimListener.Stub {
         int[] mClientId;
         ClientProfile mProfile;
@@ -125,19 +132,6 @@
         }
     }
 
-    private static final class TestResourcesReclaimListener extends IResourcesReclaimListener.Stub {
-        boolean mReclaimed;
-
-        @Override
-        public void onReclaimResources() {
-            mReclaimed = true;
-        }
-
-        public boolean isReclaimed() {
-            return mReclaimed;
-        }
-    }
-
     // A correspondence to compare a FrontendResource and a TunerFrontendInfo.
     private static final Correspondence<FrontendResource, TunerFrontendInfo> FR_TFI_COMPARE =
             Correspondence.from((FrontendResource actual, TunerFrontendInfo expected) -> {
@@ -485,6 +479,62 @@
     }
 
     @Test
+    @EnableFlags({FLAG_SET_RESOURCE_HOLDER_RETAIN})
+    public void requestFrontendTest_NoFrontendAvailable_RequestWithEqualPriority()
+            throws RemoteException {
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register(
+                "0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+        client1.register(
+                "1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+
+        // Init frontend resource.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[1];
+        infos[0] =
+                tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+        // client0 requests for 1 frontend
+        TunerFrontendRequest request =
+                tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
+        long[] frontendHandle = new long[1];
+        assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendHandle))
+                .isTrue();
+        assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
+        assertThat(client0.getProfile().getInUseFrontendHandles())
+                .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle)));
+
+        // setResourceHolderRetain sets mResourceHolderRetain to true to allow the Resource Holder
+        // (client0) to maintain ownership such as requester will not get the resources.
+        client1.getProfile().setResourceHolderRetain(true);
+
+        request = tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
+        assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendHandle))
+                .isFalse();
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle).isInUse())
+                .isTrue();
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
+                           .getOwnerClientId())
+                .isEqualTo(client0.getId());
+        assertThat(client0.isReclaimed()).isFalse();
+
+        // setResourceHolderRetain sets mResourceHolderRetain to false to allow the Resource
+        // Challenger (client1) to acquire the resource and Resource Holder loses ownership of the
+        // resources.
+        client1.getProfile().setResourceHolderRetain(false);
+
+        assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendHandle))
+                .isTrue();
+        assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
+                           .getOwnerClientId())
+                .isEqualTo(client1.getId());
+        assertThat(client0.isReclaimed()).isTrue();
+    }
+
+    @Test
     public void releaseFrontendTest_UnderTheSameExclusiveGroup() throws RemoteException {
         // Register clients
         TunerClient client0 = new TunerClient();
@@ -565,6 +615,74 @@
     }
 
     @Test
+    @EnableFlags({FLAG_SET_RESOURCE_HOLDER_RETAIN})
+    public void requestCasTest_NoCasAvailable_RequestWithEqualPriority() throws RemoteException {
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register(
+                "0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+        client1.register(
+                "1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+
+        // Init cas resources.
+        mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
+
+        CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/);
+        long[] casSessionHandle = new long[1];
+
+        // client0 requests for 2 cas sessions.
+        assertThat(
+                mTunerResourceManagerService.requestCasSessionInternal(request, casSessionHandle))
+                .isTrue();
+        assertThat(
+                mTunerResourceManagerService.requestCasSessionInternal(request, casSessionHandle))
+                .isTrue();
+        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
+                .isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCasSystemId()).isEqualTo(1);
+        assertThat(mTunerResourceManagerService.getCasResource(1).getOwnerClientIds())
+                .isEqualTo(new HashSet<Integer>(Arrays.asList(client0.getId())));
+        assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue();
+
+        // setResourceHolderRetain sets mResourceHolderRetain to true to allow the Resource Holder
+        // to maintain ownership such as requester (client1) will not get the resources.
+        client1.getProfile().setResourceHolderRetain(true);
+
+        request = casSessionRequest(client1.getId(), 1);
+        assertThat(
+                mTunerResourceManagerService.requestCasSessionInternal(request, casSessionHandle))
+                .isFalse();
+        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
+                .isEqualTo(-1);
+        assertThat(client0.getProfile().getInUseCasSystemId()).isEqualTo(1);
+        assertThat(client1.getProfile().getInUseCasSystemId())
+                .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+        assertThat(mTunerResourceManagerService.getCasResource(1).getOwnerClientIds())
+                .isEqualTo(new HashSet<Integer>(Arrays.asList(client0.getId())));
+        assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue();
+        assertThat(client0.isReclaimed()).isFalse();
+
+        // setResourceHolderRetain sets mResourceHolderRetain to false to allow the Resource
+        // Challenger (client1) to acquire the resource and Resource Holder loses ownership of the
+        // resources.
+        client1.getProfile().setResourceHolderRetain(false);
+
+        assertThat(
+                mTunerResourceManagerService.requestCasSessionInternal(request, casSessionHandle))
+                .isTrue();
+        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
+                .isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCasSystemId())
+                .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+        assertThat(client1.getProfile().getInUseCasSystemId()).isEqualTo(1);
+        assertThat(mTunerResourceManagerService.getCasResource(1).getOwnerClientIds())
+                .isEqualTo(new HashSet<Integer>(Arrays.asList(client1.getId())));
+        assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
+        assertThat(client0.isReclaimed()).isTrue();
+    }
+
+    @Test
     public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority()
             throws RemoteException {
         // Register clients
@@ -612,6 +730,71 @@
     }
 
     @Test
+    @EnableFlags({FLAG_SET_RESOURCE_HOLDER_RETAIN})
+    public void requestCiCamTest_NoCiCamAvailable_RequestWithEqualPriority()
+            throws RemoteException {
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register(
+                "0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+        client1.register(
+                "1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+
+        // Init cicam/cas resources.
+        mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
+
+        TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/);
+        long[] ciCamHandle = new long[1];
+
+        // client0 request for 2 ciCam sessions.
+        assertThat(mTunerResourceManagerService.requestCiCamInternal(request, ciCamHandle))
+                .isTrue();
+        assertThat(mTunerResourceManagerService.requestCiCamInternal(request, ciCamHandle))
+                .isTrue();
+        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
+                .isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1);
+        assertThat(mTunerResourceManagerService.getCiCamResource(1).getOwnerClientIds())
+                .isEqualTo(new HashSet<Integer>(Arrays.asList(client0.getId())));
+        assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue();
+
+        // setResourceHolderRetain sets mResourceHolderRetain to true to allow the Resource Holder
+        // (client0) to maintain ownership such as requester will not get the resources.
+        client1.getProfile().setResourceHolderRetain(true);
+
+        request = tunerCiCamRequest(client1.getId(), 1);
+        assertThat(mTunerResourceManagerService.requestCiCamInternal(request, ciCamHandle))
+                .isFalse();
+        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
+                .isEqualTo(-1);
+        assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1);
+        assertThat(client1.getProfile().getInUseCiCamId())
+                .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+        assertThat(mTunerResourceManagerService.getCiCamResource(1).getOwnerClientIds())
+                .isEqualTo(new HashSet<Integer>(Arrays.asList(client0.getId())));
+        assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue();
+        assertThat(client0.isReclaimed()).isFalse();
+
+        // setResourceHolderRetain sets mResourceHolderRetain to false to allow the Resource
+        // Challenger (client1) to acquire the resource and Resource Holder loses ownership of the
+        // resources.
+        client1.getProfile().setResourceHolderRetain(false);
+
+        assertThat(mTunerResourceManagerService.requestCiCamInternal(request, ciCamHandle))
+                .isTrue();
+        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
+                .isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCiCamId())
+                .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+        assertThat(client1.getProfile().getInUseCiCamId()).isEqualTo(1);
+        assertThat(mTunerResourceManagerService.getCiCamResource(1).getOwnerClientIds())
+                .isEqualTo(new HashSet<Integer>(Arrays.asList(client1.getId())));
+        assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+        assertThat(client0.isReclaimed()).isTrue();
+    }
+
+    @Test
     public void releaseCasTest() throws RemoteException {
         // Register clients
         TunerClient client0 = new TunerClient();
@@ -721,6 +904,59 @@
     }
 
     @Test
+    @EnableFlags({FLAG_SET_RESOURCE_HOLDER_RETAIN})
+    public void requestLnbTest_NoLnbAvailable_RequestWithEqualPriority() throws RemoteException {
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register(
+                "0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+        client1.register(
+                "1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+
+        // Init lnb resources.
+        long[] lnbHandles = {1};
+        mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
+
+        // client0 requests 1 lnb
+        TunerLnbRequest request = new TunerLnbRequest();
+        request.clientId = client0.getId();
+        long[] lnbHandle = new long[1];
+        assertThat(mTunerResourceManagerService.requestLnbInternal(request, lnbHandle)).isTrue();
+        assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
+        assertThat(client0.getProfile().getInUseLnbHandles())
+                .isEqualTo(new HashSet<Long>(Arrays.asList(lnbHandles[0])));
+
+        // setResourceHolderRetain sets mResourceHolderRetain to true to allow the Resource Holder
+        // (client0) to maintain ownership such as requester will not get the resources.
+        client1.getProfile().setResourceHolderRetain(true);
+
+        request = new TunerLnbRequest();
+        request.clientId = client1.getId();
+
+        assertThat(mTunerResourceManagerService.requestLnbInternal(request, lnbHandle)).isFalse();
+        assertThat(lnbHandle[0]).isNotEqualTo(lnbHandles[0]);
+        assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0]).isInUse()).isTrue();
+        assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0]).getOwnerClientId())
+                .isEqualTo(client0.getId());
+        assertThat(client0.isReclaimed()).isFalse();
+        assertThat(client1.getProfile().getInUseLnbHandles().size()).isEqualTo(0);
+
+        // setResourceHolderRetain sets mResourceHolderRetain to false to allow the Resource
+        // Challenger (client1) to acquire the resource and Resource Holder loses ownership of the
+        // resources.
+        client1.getProfile().setResourceHolderRetain(false);
+
+        assertThat(mTunerResourceManagerService.requestLnbInternal(request, lnbHandle)).isTrue();
+        assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
+        assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0]).isInUse()).isTrue();
+        assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0]).getOwnerClientId())
+                .isEqualTo(client1.getId());
+        assertThat(client0.isReclaimed()).isTrue();
+        assertThat(client0.getProfile().getInUseLnbHandles().size()).isEqualTo(0);
+    }
+
+    @Test
     public void releaseLnbTest() throws RemoteException {
         // Register clients
         TunerClient client0 = new TunerClient();
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index def3355..aee9f0f 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -36,18 +36,15 @@
     Map<String, Map<Integer, PackageInfo>> mPackages = new HashMap();
     private final int mNumRelros;
     private final boolean mIsDebuggable;
-    private int mMultiProcessSetting;
-    private final boolean mMultiProcessDefault;
 
     public static final int PRIMARY_USER_ID = 0;
 
-    public TestSystemImpl(WebViewProviderInfo[] packageConfigs, int numRelros, boolean isDebuggable,
-            boolean multiProcessDefault) {
+    public TestSystemImpl(WebViewProviderInfo[] packageConfigs, int numRelros,
+            boolean isDebuggable) {
         mPackageConfigs = packageConfigs;
         mNumRelros = numRelros;
         mIsDebuggable = isDebuggable;
         mUsers.add(PRIMARY_USER_ID);
-        mMultiProcessDefault = multiProcessDefault;
     }
 
     public void addUser(int userId) {
@@ -181,26 +178,8 @@
     }
 
     @Override
-    public int getMultiProcessSetting() {
-        return mMultiProcessSetting;
-    }
-
-    @Override
-    public void setMultiProcessSetting(int value) {
-        mMultiProcessSetting = value;
-    }
-
-    @Override
-    public void notifyZygote(boolean enableMultiProcess) {}
-
-    @Override
     public void ensureZygoteStarted() {}
 
     @Override
-    public boolean isMultiProcessDefaultEnabled() {
-        return mMultiProcessDefault;
-    }
-
-    @Override
     public void pinWebviewIfRequired(ApplicationInfo appInfo) {}
 }
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 06479c8..42eb609 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.webkit;
 
-import static android.webkit.Flags.updateServiceV2;
-
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -27,8 +25,6 @@
 import android.content.pm.Signature;
 import android.os.Build;
 import android.os.Bundle;
-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.util.Base64;
@@ -62,7 +58,7 @@
 public class WebViewUpdateServiceTest {
     private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
 
-    private WebViewUpdateServiceInterface mWebViewUpdateServiceImpl;
+    private WebViewUpdateServiceImpl2 mWebViewUpdateServiceImpl;
     private TestSystemImpl mTestSystemImpl;
 
     private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
@@ -77,38 +73,23 @@
     }
 
     private void setupWithPackages(WebViewProviderInfo[] packages) {
-        setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
-                false /* multiProcessDefault */);
+        setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */);
     }
 
     private void setupWithPackagesAndRelroCount(WebViewProviderInfo[] packages, int numRelros) {
-        setupWithAllParameters(packages, numRelros, true /* isDebuggable */,
-                false /* multiProcessDefault */);
+        setupWithAllParameters(packages, numRelros, true /* isDebuggable */);
     }
 
     private void setupWithPackagesNonDebuggable(WebViewProviderInfo[] packages) {
-        setupWithAllParameters(packages, 1 /* numRelros */, false /* isDebuggable */,
-                false /* multiProcessDefault */);
-    }
-
-    private void setupWithPackagesAndMultiProcess(WebViewProviderInfo[] packages,
-            boolean multiProcessDefault) {
-        setupWithAllParameters(packages, 1 /* numRelros */, true /* isDebuggable */,
-                multiProcessDefault);
+        setupWithAllParameters(packages, 1 /* numRelros */, false /* isDebuggable */);
     }
 
     private void setupWithAllParameters(WebViewProviderInfo[] packages, int numRelros,
-            boolean isDebuggable, boolean multiProcessDefault) {
-        TestSystemImpl testing = new TestSystemImpl(packages, numRelros, isDebuggable,
-                multiProcessDefault);
+            boolean isDebuggable) {
+        TestSystemImpl testing = new TestSystemImpl(packages, numRelros, isDebuggable);
         mTestSystemImpl = Mockito.spy(testing);
-        if (updateServiceV2()) {
-            mWebViewUpdateServiceImpl =
-                    new WebViewUpdateServiceImpl2(mTestSystemImpl);
-        } else {
-            mWebViewUpdateServiceImpl =
-                    new WebViewUpdateServiceImpl(mTestSystemImpl);
-        }
+        mWebViewUpdateServiceImpl =
+                new WebViewUpdateServiceImpl2(mTestSystemImpl);
     }
 
     private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
@@ -350,24 +331,6 @@
     }
 
     @Test
-    @RequiresFlagsDisabled("android.webkit.update_service_v2")
-    // If the flag is set, will throw an exception because of no available by default provider.
-    public void testEmptyConfig() {
-        WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
-        setupWithPackages(packages);
-        setEnabledAndValidPackageInfos(packages);
-
-        runWebViewBootPreparationOnMainSync();
-
-        Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
-                Matchers.anyObject());
-
-        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
-        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
-        assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage());
-    }
-
-    @Test
     public void testFailListingEmptyWebviewPackages() {
         String singlePackage = "singlePackage";
         WebViewProviderInfo[] packages = new WebViewProviderInfo[]{
@@ -554,73 +517,6 @@
         }
     }
 
-    /**
-     * Scenario for testing re-enabling a fallback package.
-     */
-    @Test
-    @RequiresFlagsDisabled("android.webkit.update_service_v2")
-    public void testFallbackPackageEnabling() {
-        String testPackage = "testFallback";
-        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
-            new WebViewProviderInfo(
-                    testPackage, "", true /* default available */, true /* fallback */, null)};
-        setupWithPackages(packages);
-        mTestSystemImpl.setPackageInfo(
-                createPackageInfo(testPackage, false /* enabled */ , true /* valid */,
-                    true /* installed */));
-
-        // Check that the boot time logic re-enables the fallback package.
-        runWebViewBootPreparationOnMainSync();
-        Mockito.verify(mTestSystemImpl).enablePackageForAllUsers(
-                Mockito.eq(testPackage), Mockito.eq(true));
-
-        // Fake the message about the enabling having changed the package state,
-        // and check we now use that package.
-        mWebViewUpdateServiceImpl.packageStateChanged(
-                testPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID);
-        checkPreparationPhasesForPackage(testPackage, 1);
-    }
-
-    /**
-     * Scenario for installing primary package when secondary in use.
-     * 1. Start with only secondary installed
-     * 2. Install primary
-     * 3. Primary should be used
-     */
-    @Test
-    @RequiresFlagsDisabled("android.webkit.update_service_v2")
-    // If the flag is set, we don't automitally switch to secondary package unless it is
-    // chosen directly.
-    public void testInstallingPrimaryPackage() {
-        String primaryPackage = "primary";
-        String secondaryPackage = "secondary";
-        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
-            new WebViewProviderInfo(
-                    primaryPackage, "", true /* default available */, false /* fallback */, null),
-            new WebViewProviderInfo(
-                    secondaryPackage, "", true /* default available */, false /* fallback */,
-                    null)};
-        setupWithPackages(packages);
-        mTestSystemImpl.setPackageInfo(
-                createPackageInfo(secondaryPackage, true /* enabled */ , true /* valid */,
-                    true /* installed */));
-
-        runWebViewBootPreparationOnMainSync();
-        checkPreparationPhasesForPackage(secondaryPackage,
-                1 /* first preparation for this package*/);
-
-        // Install primary package
-        mTestSystemImpl.setPackageInfo(
-                createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */,
-                    true /* installed */));
-        mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
-                WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID);
-
-        // Verify primary package used as provider, and secondary package killed
-        checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation for this package*/);
-        Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(secondaryPackage));
-    }
-
     @Test
     public void testRemovingSecondarySelectsPrimarySingleUser() {
         for (PackageRemovalType removalType : REMOVAL_TYPES) {
@@ -848,14 +744,6 @@
         checkRecoverAfterFailListingWebviewPackages(true);
     }
 
-    @Test
-    @RequiresFlagsDisabled("android.webkit.update_service_v2")
-    // If the flag is set, we don't automitally switch to second package unless it is chosen
-    // directly.
-    public void testRecoverFailedListingWebViewPackagesAddedPackage() {
-        checkRecoverAfterFailListingWebviewPackages(false);
-    }
-
     /**
      * Test that we can recover correctly from failing to list WebView packages.
      * settingsChange: whether to fail during changeProviderAndSetting or packageStateChanged
@@ -1114,31 +1002,6 @@
         }
     }
 
-    /**
-     * Ensure that the update service does not use an uninstalled package even if it is the only
-     * package available.
-     */
-    @Test
-    @RequiresFlagsDisabled("android.webkit.update_service_v2")
-    // If the flag is set, we return the package even if it is not installed.
-    public void testWithSingleUninstalledPackage() {
-        String testPackageName = "test.package.name";
-        WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
-                new WebViewProviderInfo(testPackageName, "",
-                        true /*default available*/, false /* fallback */, null)};
-        setupWithPackages(webviewPackages);
-        mTestSystemImpl.setPackageInfo(createPackageInfo(testPackageName, true /* enabled */,
-                    true /* valid */, false /* installed */));
-
-        runWebViewBootPreparationOnMainSync();
-
-        Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
-                Matchers.anyObject());
-        WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
-        assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
-        assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage());
-    }
-
     @Test
     public void testNonhiddenPackageUserOverHidden() {
         checkVisiblePackageUserOverNonVisible(false /* multiUser*/, PackageRemovalType.HIDE);
@@ -1374,95 +1237,6 @@
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName);
     }
 
-    @Test
-    @RequiresFlagsDisabled("android.webkit.update_service_v2")
-    public void testMultiProcessEnabledByDefault() {
-        testMultiProcessByDefault(true /* enabledByDefault */);
-    }
-
-    @Test
-    @RequiresFlagsDisabled("android.webkit.update_service_v2")
-    public void testMultiProcessDisabledByDefault() {
-        testMultiProcessByDefault(false /* enabledByDefault */);
-    }
-
-    private void testMultiProcessByDefault(boolean enabledByDefault) {
-        String primaryPackage = "primary";
-        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
-            new WebViewProviderInfo(
-                    primaryPackage, "", true /* default available */, false /* fallback */, null)};
-        setupWithPackagesAndMultiProcess(packages, enabledByDefault);
-        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
-                    true /* valid */, true /* installed */, null /* signatures */,
-                    10 /* lastUpdateTime*/, false /* not hidden */, 1000 /* versionCode */,
-                    false /* isSystemApp */));
-
-        runWebViewBootPreparationOnMainSync();
-        checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
-
-        // Check it's off by default
-        assertEquals(enabledByDefault, mWebViewUpdateServiceImpl.isMultiProcessEnabled());
-
-        // Test toggling it
-        mWebViewUpdateServiceImpl.enableMultiProcess(!enabledByDefault);
-        assertEquals(!enabledByDefault, mWebViewUpdateServiceImpl.isMultiProcessEnabled());
-        mWebViewUpdateServiceImpl.enableMultiProcess(enabledByDefault);
-        assertEquals(enabledByDefault, mWebViewUpdateServiceImpl.isMultiProcessEnabled());
-    }
-
-    @Test
-    @RequiresFlagsDisabled("android.webkit.update_service_v2")
-    public void testMultiProcessEnabledByDefaultWithSettingsValue() {
-        testMultiProcessByDefaultWithSettingsValue(
-                true /* enabledByDefault */, Integer.MIN_VALUE, false /* expectEnabled */);
-        testMultiProcessByDefaultWithSettingsValue(
-                true /* enabledByDefault */, -999999, true /* expectEnabled */);
-        testMultiProcessByDefaultWithSettingsValue(
-                true /* enabledByDefault */, 0, true /* expectEnabled */);
-        testMultiProcessByDefaultWithSettingsValue(
-                true /* enabledByDefault */, 999999, true /* expectEnabled */);
-    }
-
-    @Test
-    @RequiresFlagsDisabled("android.webkit.update_service_v2")
-    public void testMultiProcessDisabledByDefaultWithSettingsValue() {
-        testMultiProcessByDefaultWithSettingsValue(
-                false /* enabledByDefault */, Integer.MIN_VALUE, false /* expectEnabled */);
-        testMultiProcessByDefaultWithSettingsValue(
-                false /* enabledByDefault */, 0, false /* expectEnabled */);
-        testMultiProcessByDefaultWithSettingsValue(
-                false /* enabledByDefault */, 999999, false /* expectEnabled */);
-        testMultiProcessByDefaultWithSettingsValue(
-                false /* enabledByDefault */, Integer.MAX_VALUE, true /* expectEnabled */);
-    }
-
-    /**
-     * Test the logic of the multiprocess setting depending on whether multiprocess is enabled by
-     * default, and what the setting is set to.
-     * @param enabledByDefault whether multiprocess is enabled by default.
-     * @param settingValue value of the multiprocess setting.
-     */
-    private void testMultiProcessByDefaultWithSettingsValue(
-            boolean enabledByDefault, int settingValue, boolean expectEnabled) {
-        String primaryPackage = "primary";
-        WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
-            new WebViewProviderInfo(
-                    primaryPackage, "", true /* default available */, false /* fallback */, null)};
-        setupWithPackagesAndMultiProcess(packages, enabledByDefault);
-        mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */,
-                    true /* valid */, true /* installed */, null /* signatures */,
-                    10 /* lastUpdateTime*/, false /* not hidden */, 1000 /* versionCode */,
-                    false /* isSystemApp */));
-
-        runWebViewBootPreparationOnMainSync();
-        checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
-
-        mTestSystemImpl.setMultiProcessSetting(settingValue);
-
-        assertEquals(expectEnabled, mWebViewUpdateServiceImpl.isMultiProcessEnabled());
-    }
-
-
     /**
      * Ensure that packages with a targetSdkVersion targeting the current platform are valid, and
      * that packages targeting an older version are not valid.
@@ -1507,7 +1281,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled("android.webkit.update_service_v2")
     public void testDefaultWebViewPackageIsTheFirstAvailableByDefault() {
         String nonDefaultPackage = "nonDefaultPackage";
         String defaultPackage1 = "defaultPackage1";
@@ -1524,7 +1297,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled("android.webkit.update_service_v2")
     public void testDefaultWebViewPackageEnabling() {
         String testPackage = "testDefault";
         WebViewProviderInfo[] packages =
@@ -1548,7 +1320,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled("android.webkit.update_service_v2")
     public void testDefaultWebViewPackageInstallingDuringStartUp() {
         String testPackage = "testDefault";
         WebViewProviderInfo[] packages =
@@ -1572,7 +1343,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled("android.webkit.update_service_v2")
     public void testDefaultWebViewPackageInstallingAfterStartUp() {
         String testPackage = "testDefault";
         WebViewProviderInfo[] packages =
@@ -1603,7 +1373,6 @@
      * the repair logic.
      */
     @Test
-    @RequiresFlagsEnabled("android.webkit.update_service_v2")
     public void testAddingNewUserWithDefaultdPackageNotInstalled() {
         String testPackage = "testDefault";
         WebViewProviderInfo[] packages =
@@ -1651,7 +1420,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled("android.webkit.update_service_v2")
     public void testDisabledDefaultPackageChosen() {
         PackageInfo disabledPackage =
                 createPackageInfo(
@@ -1664,7 +1432,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled("android.webkit.update_service_v2")
     public void testUninstalledDefaultPackageChosen() {
         PackageInfo uninstalledPackage =
                 createPackageInfo(
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 dec7f09..cbfdc5f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -77,6 +77,8 @@
 import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
 import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.PackageManager.FEATURE_TELECOM;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -92,6 +94,7 @@
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
+import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
 import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
 import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
 import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
@@ -109,6 +112,7 @@
 import static android.service.notification.Condition.SOURCE_USER_ACTION;
 import static android.service.notification.Condition.STATE_TRUE;
 import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT;
 import static android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING;
 import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
@@ -262,6 +266,7 @@
 import android.permission.PermissionManager;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.platform.test.rule.LimitDevicesRule;
@@ -361,6 +366,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.OutputStream;
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -481,6 +487,15 @@
 
     NotificationChannel mMinChannel = new NotificationChannel("min", "min", IMPORTANCE_MIN);
 
+    private final NotificationChannel mParentChannel =
+            new NotificationChannel(PARENT_CHANNEL_ID, "parentName", IMPORTANCE_DEFAULT);
+    private final NotificationChannel mConversationChannel =
+            new NotificationChannel(CONVERSATION_CHANNEL_ID, "conversationName", IMPORTANCE_DEFAULT);
+
+    private static final String PARENT_CHANNEL_ID = "parentChannelId";
+    private static final String CONVERSATION_CHANNEL_ID = "conversationChannelId";
+    private static final String CONVERSATION_ID = "conversationId";
+
     private static final int NOTIFICATION_LOCATION_UNKNOWN = 0;
 
     private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
@@ -2880,6 +2895,44 @@
     }
 
     @Test
+    @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+            android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+    public void testRemoveScheduledForceGroup_onNotificationCanceled() throws Exception {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "tag", null,
+                false);
+        when(mGroupHelper.onNotificationPosted(any(), anyBoolean())).thenReturn(false);
+        mService.addEnqueuedNotification(r);
+        NotificationManagerService.PostNotificationRunnable runnable =
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                r.getUid(), mPostNotificationTrackerFactory.newTracker(null));
+        runnable.run();
+        waitForIdle();
+
+        // Post an update to the notification
+        NotificationRecord r_update =
+                generateNotificationRecord(mTestNotificationChannel, 0, "tag", null, false);
+        mService.addEnqueuedNotification(r_update);
+        runnable = mService.new PostNotificationRunnable(r_update.getKey(),
+                r_update.getSbn().getPackageName(), r_update.getUid(),
+                mPostNotificationTrackerFactory.newTracker(null));
+        runnable.run();
+        waitForIdle();
+
+        // Cancel the notification
+        mBinderService.cancelNotificationWithTag(r.getSbn().getPackageName(),
+                r.getSbn().getPackageName(), r.getSbn().getTag(),
+                r.getSbn().getId(), r.getSbn().getUserId());
+        waitForIdle();
+
+        mTestableLooper.moveTimeForward(DELAY_FORCE_REGROUP_TIME);
+        waitForIdle();
+
+        // Check that onNotificationPostedWithDelay was canceled
+        verify(mGroupHelper, times(1)).onNotificationPosted(any(), anyBoolean());
+        verify(mGroupHelper, never()).onNotificationPostedWithDelay(any(), any(), any());
+    }
+
+    @Test
     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
     public void testEnqueueNotification_forceGrouped_clearsSummaryFlag() throws Exception {
         final String originalGroupName = "originalGroup";
@@ -4668,8 +4721,161 @@
         verify(mAmi).hasForegroundServiceNotification(anyString(), anyInt(), anyString());
     }
 
+    private void setUpChannelsForConversationChannelTest() throws RemoteException {
+        when(mPreferencesHelper.getNotificationChannel(
+                eq(mPkg), eq(mUid), eq(PARENT_CHANNEL_ID), eq(false)))
+                .thenReturn(mParentChannel);
+        when(mPreferencesHelper.getConversationNotificationChannel(
+                eq(mPkg), eq(mUid), eq(PARENT_CHANNEL_ID), eq(CONVERSATION_ID), eq(false), eq(false)))
+                .thenReturn(mConversationChannel);
+        when(mPackageManager.getPackageUid(mPkg, 0, mUserId)).thenReturn(mUid);
+    }
+
     @Test
-    public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
+    @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+    public void createConversationChannelForPkgFromPrivilegedListener_cdm_success() throws Exception {
+        // Set up cdm
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(mPkg, mUserId))
+                .thenReturn(singletonList(mock(AssociationInfo.class)));
+
+        // Set up parent channel
+        setUpChannelsForConversationChannelTest();
+        final NotificationChannel parentChannelCopy = mParentChannel.copy();
+
+        NotificationChannel createdChannel =
+                mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+                    null, mPkg, mUser, PARENT_CHANNEL_ID, CONVERSATION_ID);
+
+        // Verify that a channel is created and a copied channel is returned.
+        verify(mPreferencesHelper, times(1)).createNotificationChannel(
+                eq(mPkg), eq(mUid), any(), anyBoolean(), anyBoolean(),
+                eq(mUid), anyBoolean());
+        assertThat(createdChannel).isNotSameInstanceAs(mConversationChannel);
+        assertThat(createdChannel).isEqualTo(mConversationChannel);
+
+        // Verify that the channel creation is not directly use the parent channel.
+        verify(mPreferencesHelper, never()).createNotificationChannel(
+                anyString(), anyInt(), eq(mParentChannel), anyBoolean(), anyBoolean(),
+                anyInt(), anyBoolean());
+
+        // Verify that the content of parent channel is not changed.
+        assertThat(parentChannelCopy).isEqualTo(mParentChannel);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+    public void createConversationChannelForPkgFromPrivilegedListener_cdm_noAccess() throws Exception {
+        // Set up cdm without access
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(mPkg, mUserId))
+                .thenReturn(emptyList());
+
+        // Set up parent channel
+        setUpChannelsForConversationChannelTest();
+
+        try {
+            mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+                null, mPkg, mUser, "parentId", "conversationId");
+            fail("listeners that don't have a companion device shouldn't be able to call this");
+        } catch (SecurityException e) {
+            // pass
+        }
+
+        verify(mPreferencesHelper, never()).createNotificationChannel(
+                anyString(), anyInt(), any(), anyBoolean(), anyBoolean(),
+                anyInt(), anyBoolean());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+    public void createConversationChannelForPkgFromPrivilegedListener_assistant_success() throws Exception {
+        // Set up assistant
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(mPkg, mUserId))
+                .thenReturn(emptyList());
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+
+        // Set up parent channel
+        setUpChannelsForConversationChannelTest();
+        final NotificationChannel parentChannelCopy = mParentChannel.copy();
+
+        NotificationChannel createdChannel =
+                mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+                    null, mPkg, mUser, PARENT_CHANNEL_ID, CONVERSATION_ID);
+
+        // Verify that a channel is created and a copied channel is returned.
+        verify(mPreferencesHelper, times(1)).createNotificationChannel(
+                eq(mPkg), eq(mUid), any(), anyBoolean(), anyBoolean(),
+                eq(mUid), anyBoolean());
+        assertThat(createdChannel).isNotSameInstanceAs(mConversationChannel);
+        assertThat(createdChannel).isEqualTo(mConversationChannel);
+
+        // Verify that the channel creation is not directly use the parent channel.
+        verify(mPreferencesHelper, never()).createNotificationChannel(
+                anyString(), anyInt(), eq(mParentChannel), anyBoolean(), anyBoolean(),
+                anyInt(), anyBoolean());
+
+        // Verify that the content of parent channel is not changed.
+        assertThat(parentChannelCopy).isEqualTo(mParentChannel);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+    public void createConversationChannelForPkgFromPrivilegedListener_assistant_noAccess() throws Exception {
+        // Set up assistant without access
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(mPkg, mUserId))
+                .thenReturn(emptyList());
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
+
+        // Set up parent channel
+        setUpChannelsForConversationChannelTest();
+
+        try {
+            mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+                null, mPkg, mUser, "parentId", "conversationId");
+            fail("listeners that don't have a companion device shouldn't be able to call this");
+        } catch (SecurityException e) {
+            // pass
+        }
+
+        verify(mPreferencesHelper, never()).createNotificationChannel(
+                anyString(), anyInt(), any(), anyBoolean(), anyBoolean(),
+                anyInt(), anyBoolean());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_NOTIFICATION_CONVERSATION_CHANNEL_MANAGEMENT)
+    public void createConversationChannelForPkgFromPrivilegedListener_badUser() throws Exception {
+        // Set up bad user
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(mPkg, mUserId))
+                .thenReturn(singletonList(mock(AssociationInfo.class)));
+        mListener = mock(ManagedServices.ManagedServiceInfo.class);
+        mListener.component = new ComponentName(mPkg, mPkg);
+        when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+        // Set up parent channel
+        setUpChannelsForConversationChannelTest();
+
+        try {
+            mBinderService.createConversationNotificationChannelForPackageFromPrivilegedListener(
+                null, mPkg, mUser, "parentId", "conversationId");
+            fail("listener getting channels from a user they cannot see");
+        } catch (SecurityException e) {
+            // pass
+        }
+
+        verify(mPreferencesHelper, never()).createNotificationChannel(
+                anyString(), anyInt(), any(), anyBoolean(), anyBoolean(),
+                anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void updateNotificationChannelFromPrivilegedListener_cdm_success() throws Exception {
+
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mCompanionMgr.getAssociations(mPkg, mUserId))
                 .thenReturn(singletonList(mock(AssociationInfo.class)));
@@ -4689,7 +4895,7 @@
     }
 
     @Test
-    public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
+    public void updateNotificationChannelFromPrivilegedListener_cdm_noAccess() throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mCompanionMgr.getAssociations(mPkg, mUserId))
                 .thenReturn(emptyList());
@@ -4711,7 +4917,51 @@
     }
 
     @Test
-    public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
+    public void updateNotificationChannelFromPrivilegedListener_assistant_success() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(mPkg, mUserId))
+                .thenReturn(emptyList());
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+        when(mPreferencesHelper.getNotificationChannel(eq(mPkg), anyInt(),
+                eq(mTestNotificationChannel.getId()), anyBoolean()))
+                .thenReturn(mTestNotificationChannel);
+
+        mBinderService.updateNotificationChannelFromPrivilegedListener(
+                null, mPkg, Process.myUserHandle(), mTestNotificationChannel);
+
+        verify(mPreferencesHelper, times(1)).updateNotificationChannel(
+                anyString(), anyInt(), any(), anyBoolean(),  anyInt(), anyBoolean());
+
+        verify(mListeners, never()).notifyNotificationChannelChanged(eq(mPkg),
+                eq(Process.myUserHandle()), eq(mTestNotificationChannel),
+                eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+    }
+
+    @Test
+    public void updateNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(mPkg, mUserId))
+                .thenReturn(emptyList());
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
+
+        try {
+            mBinderService.updateNotificationChannelFromPrivilegedListener(
+                    null, mPkg, Process.myUserHandle(), mTestNotificationChannel);
+            fail("listeners that don't have a companion device shouldn't be able to call this");
+        } catch (SecurityException e) {
+            // pass
+        }
+
+        verify(mPreferencesHelper, never()).updateNotificationChannel(
+                anyString(), anyInt(), any(), anyBoolean(),  anyInt(), anyBoolean());
+
+        verify(mListeners, never()).notifyNotificationChannelChanged(eq(mPkg),
+                eq(Process.myUserHandle()), eq(mTestNotificationChannel),
+                eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+    }
+
+    @Test
+    public void updateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mCompanionMgr.getAssociations(mPkg, mUserId))
                 .thenReturn(singletonList(mock(AssociationInfo.class)));
@@ -4737,7 +4987,7 @@
     }
 
     @Test
-    public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission()
+    public void updateNotificationChannelFromPrivilegedListener_noSoundUriPermission()
             throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mCompanionMgr.getAssociations(mPkg, mUserId))
@@ -4769,7 +5019,7 @@
     }
 
     @Test
-    public void testUpdateNotificationChannelFromPrivilegedListener_noSoundUriPermission_sameSound()
+    public void updateNotificationChannelFromPrivilegedListener_noSoundUriPermission_sameSound()
             throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mCompanionMgr.getAssociations(mPkg, mUserId))
@@ -4801,7 +5051,7 @@
 
     @Test
     public void
-        testUpdateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm()
+        updateNotificationChannelFromPrivilegedListener_oldSoundNoUriPerm_newSoundHasUriPerm()
             throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mCompanionMgr.getAssociations(mPkg, mUserId))
@@ -6499,6 +6749,35 @@
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING)
+    public void testReadPolicyXml_backupRestoreLogging() throws Exception {
+        BackupRestoreEventLogger logger = mock(BackupRestoreEventLogger.class);
+
+        UserInfo ui = new UserInfo(ActivityManager.getCurrentUser(), "Clone", UserInfo.FLAG_FULL);
+        ui.userType = USER_TYPE_FULL_SYSTEM;
+        when(mUmInternal.getUserInfo(ActivityManager.getCurrentUser())).thenReturn(ui);
+        when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(new ArrayMap<>());
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+        mService.writePolicyXml(baos, true, ActivityManager.getCurrentUser(), logger);
+        serializer.flush();
+
+        mService.readPolicyXml(
+                new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
+                true, ActivityManager.getCurrentUser(), logger);
+
+        verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_CONFIG, 1);
+        verify(logger, never())
+                .logItemsBackupFailed(eq(DATA_TYPE_ZEN_CONFIG), anyInt(), anyString());
+
+        verify(logger).logItemsRestored(DATA_TYPE_ZEN_CONFIG, 1);
+        verify(logger, never())
+                .logItemsRestoreFailed(eq(DATA_TYPE_ZEN_CONFIG), anyInt(), anyString());
+    }
+
+    @Test
     public void testLocaleChangedCallsUpdateDefaultZenModeRules() throws Exception {
         ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class);
         mService.mZenModeHelper = mZenModeHelper;
@@ -7662,7 +7941,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);
@@ -7682,7 +7961,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);
@@ -7699,7 +7978,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);
@@ -7717,7 +7996,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);
@@ -7736,7 +8015,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
@@ -7756,7 +8035,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);
@@ -10398,7 +10677,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());
     }
 
@@ -10420,7 +10699,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());
     }
 
@@ -10440,9 +10719,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
@@ -10459,7 +10738,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
@@ -10494,7 +10774,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
@@ -10527,7 +10808,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(
@@ -10555,7 +10837,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());
     }
 
@@ -10567,7 +10849,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());
     }
 
@@ -10579,7 +10861,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());
     }
 
@@ -10601,7 +10883,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());
     }
 
@@ -10623,7 +10905,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());
     }
 
@@ -10648,7 +10930,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());
     }
 
@@ -10663,7 +10945,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());
     }
 
@@ -10678,7 +10960,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
@@ -10692,10 +10974,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);
@@ -15815,7 +16122,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
@@ -15831,7 +16139,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
@@ -15878,9 +16186,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));
         }
     }
@@ -15898,7 +16206,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
@@ -15913,7 +16221,7 @@
 
         mBinderService.getNotificationPolicy("package");
 
-        verify(zenHelper).getNotificationPolicyFromImplicitZenRule(eq("package"));
+        verify(zenHelper).getNotificationPolicyFromImplicitZenRule(any(), eq("package"));
     }
 
     @Test
@@ -15928,7 +16236,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));
     }
 
@@ -15945,9 +16253,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
@@ -15991,10 +16298,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));
         }
     }
@@ -16013,8 +16320,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
@@ -16031,9 +16338,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
@@ -16111,8 +16418,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.
@@ -16142,8 +16449,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();
 
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 1a1da0f..e1b478c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -359,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);
 
@@ -493,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 {
@@ -2632,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();
 
@@ -2646,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();
 
@@ -2656,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();
     }
@@ -2685,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();
 
@@ -2699,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();
     }
@@ -2719,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();
 
@@ -2733,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();
 
@@ -2743,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();
     }
@@ -2767,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
@@ -2783,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();
     }
@@ -2798,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
@@ -2809,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();
     }
@@ -2824,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
@@ -2835,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();
     }
@@ -2855,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();
 
@@ -2867,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();
 
@@ -2879,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();
     }
@@ -2892,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();
     }
@@ -2907,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();
     }
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/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/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 5709d88..3236f95 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -17,6 +17,8 @@
 package com.android.server.notification;
 
 import static android.app.AutomaticZenRule.TYPE_BEDTIME;
+import static android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING;
+import static android.app.Flags.FLAG_MODES_API;
 import static android.app.Flags.FLAG_MODES_UI;
 import static android.app.Flags.modesUi;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
@@ -24,6 +26,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.suppressedEffectsToString;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
 import static android.service.notification.Condition.SOURCE_UNKNOWN;
@@ -52,17 +56,22 @@
 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.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.app.NotificationManager.Policy;
+import android.app.backup.BackupRestoreEventLogger;
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Parcel;
+import android.os.UserHandle;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.FlagsParameterization;
@@ -135,7 +144,7 @@
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
         return FlagsParameterization.allCombinationsOf(
-                FLAG_MODES_UI);
+                FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING);
     }
 
     public ZenModeConfigTest(FlagsParameterization flags) {
@@ -144,7 +153,6 @@
 
     @Before
     public final void setUp() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         MockitoAnnotations.initMocks(this);
         mContext.setMockPackageManager(mPm);
     }
@@ -515,6 +523,98 @@
     }
 
     @Test
+    @EnableFlags({FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING})
+    public void testBackupRestore_fromPreModesUi() throws IOException, XmlPullParserException {
+        String xml = "<zen version=\"12\">\n"
+                + "<allow calls=\"true\" repeatCallers=\"true\" messages=\"true\""
+                + " reminders=\"false\" events=\"false\" callsFrom=\"2\" messagesFrom=\"2\""
+                + " alarms=\"true\" media=\"true\" system=\"false\" convos=\"true\""
+                + " convosFrom=\"2\" priorityChannelsAllowed=\"true\" />\n"
+                + "<disallow visualEffects=\"157\" />\n"
+                + "<manual enabled=\"true\" zen=\"1\" creationTime=\"0\" modified=\"false\" />\n"
+                + "<state areChannelsBypassingDnd=\"true\" />\n"
+                + "</zen>";
+
+        BackupRestoreEventLogger logger = mock(BackupRestoreEventLogger.class);
+        readConfigXml(new ByteArrayInputStream(xml.getBytes()), logger);
+
+        verify(logger).logItemsRestored(DATA_TYPE_ZEN_RULES, 1);
+    }
+
+    @Test
+    public void testBackupRestore() throws IOException, XmlPullParserException {
+        ZenModeConfig config = new ZenModeConfig();
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.configurationActivity = CONFIG_ACTIVITY;
+        rule.component = OWNER;
+        rule.conditionId = CONDITION_ID;
+        rule.condition = CONDITION;
+        rule.enabled = ENABLED;
+        rule.creationTime = 123;
+        rule.id = "id";
+        rule.zenMode = INTERRUPTION_FILTER;
+        rule.modified = true;
+        rule.name = NAME;
+        rule.setConditionOverride(OVERRIDE_DEACTIVATE);
+        rule.pkg = OWNER.getPackageName();
+        rule.zenPolicy = POLICY;
+
+        rule.allowManualInvocation = ALLOW_MANUAL;
+        rule.type = TYPE;
+        rule.userModifiedFields = 16;
+        rule.zenPolicyUserModifiedFields = 5;
+        rule.zenDeviceEffectsUserModifiedFields = 2;
+        rule.iconResName = ICON_RES_NAME;
+        rule.triggerDescription = TRIGGER_DESC;
+        rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
+        if (Flags.modesUi()) {
+            rule.disabledOrigin = ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+        }
+        config.automaticRules.put(rule.id, rule);
+
+        BackupRestoreEventLogger logger = null;
+        if (Flags.backupRestoreLogging()) {
+            logger = mock(BackupRestoreEventLogger.class);
+        }
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeConfigXml(config, XML_VERSION_MODES_API, true, baos, logger);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        ZenModeConfig fromXml = readConfigXml(bais, logger);
+
+        ZenModeConfig.ZenRule ruleActual = fromXml.automaticRules.get(rule.id);
+        assertEquals(rule.pkg, ruleActual.pkg);
+        assertEquals(OVERRIDE_NONE, ruleActual.getConditionOverride());
+        assertEquals(rule.enabler, ruleActual.enabler);
+        assertEquals(rule.component, ruleActual.component);
+        assertEquals(rule.configurationActivity, ruleActual.configurationActivity);
+        assertEquals(rule.condition, ruleActual.condition);
+        assertEquals(rule.enabled, ruleActual.enabled);
+        assertEquals(rule.creationTime, ruleActual.creationTime);
+        assertEquals(rule.modified, ruleActual.modified);
+        assertEquals(rule.conditionId, ruleActual.conditionId);
+        assertEquals(rule.name, ruleActual.name);
+        assertEquals(rule.zenMode, ruleActual.zenMode);
+
+        assertEquals(rule.allowManualInvocation, ruleActual.allowManualInvocation);
+        assertEquals(rule.iconResName, ruleActual.iconResName);
+        assertEquals(rule.type, ruleActual.type);
+        assertEquals(rule.userModifiedFields, ruleActual.userModifiedFields);
+        assertEquals(rule.zenPolicyUserModifiedFields, ruleActual.zenPolicyUserModifiedFields);
+        assertEquals(rule.zenDeviceEffectsUserModifiedFields,
+                ruleActual.zenDeviceEffectsUserModifiedFields);
+        assertEquals(rule.triggerDescription, ruleActual.triggerDescription);
+        assertEquals(rule.zenPolicy, ruleActual.zenPolicy);
+        assertEquals(rule.deletionInstant, ruleActual.deletionInstant);
+        if (Flags.modesUi()) {
+            assertEquals(rule.disabledOrigin, ruleActual.disabledOrigin);
+        }
+        if (Flags.backupRestoreLogging()) {
+            verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_RULES, 2);
+            verify(logger).logItemsRestored(DATA_TYPE_ZEN_RULES, 2);
+        }
+    }
+
+    @Test
     public void testWriteToParcel() {
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
         rule.configurationActivity = CONFIG_ACTIVITY;
@@ -1023,9 +1123,9 @@
 
         // write out entire config xml
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos);
+        writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos, null);
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-        ZenModeConfig fromXml = readConfigXml(bais);
+        ZenModeConfig fromXml = readConfigXml(bais, null);
 
 
         // The result should be valid and contain a manual rule; the rule should have a non-null
@@ -1055,9 +1155,9 @@
 
         // write out entire config xml
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos);
+        writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos, null);
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-        ZenModeConfig fromXml = readConfigXml(bais);
+        ZenModeConfig fromXml = readConfigXml(bais, null);
 
         // The result should have a manual rule; it should have a non-null ZenPolicy and a condition
         // whose state is true. The conditionId and enabler data should also be preserved.
@@ -1084,9 +1184,9 @@
 
         // write out entire config xml
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos);
+        writeConfigXml(config, XML_VERSION_MODES_API, /* forBackup= */ false, baos, null);
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-        ZenModeConfig fromXml = readConfigXml(bais);
+        ZenModeConfig fromXml = readConfigXml(bais, null);
 
         // The result should have a manual rule; it should not be changed from the previous rule.
         assertThat(fromXml.manualRule).isEqualTo(config.manualRule);
@@ -1213,9 +1313,9 @@
         config.manualRule.enabled = false;
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        writeConfigXml(config, XML_VERSION_MODES_UI, /* forBackup= */ false, baos);
+        writeConfigXml(config, XML_VERSION_MODES_UI, /* forBackup= */ false, baos, null);
         ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-        ZenModeConfig fromXml = readConfigXml(bais);
+        ZenModeConfig fromXml = readConfigXml(bais, null);
 
         assertThat(fromXml.manualRule.enabled).isTrue();
     }
@@ -1359,23 +1459,23 @@
     }
 
     private void writeConfigXml(ZenModeConfig config, Integer version, boolean forBackup,
-            ByteArrayOutputStream os) throws IOException {
+            ByteArrayOutputStream os, BackupRestoreEventLogger logger) throws IOException {
         String tag = ZEN_TAG;
 
         TypedXmlSerializer out = Xml.newFastSerializer();
         out.setOutput(new BufferedOutputStream(os), "utf-8");
         out.startDocument(null, true);
         out.startTag(null, tag);
-        config.writeXml(out, version, forBackup);
+        config.writeXml(out, version, forBackup, logger);
         out.endTag(null, tag);
         out.endDocument();
     }
 
-    private ZenModeConfig readConfigXml(ByteArrayInputStream is)
+    private ZenModeConfig readConfigXml(ByteArrayInputStream is, BackupRestoreEventLogger logger)
             throws XmlPullParserException, IOException {
         TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(is), null);
         parser.nextTag();
-        return ZenModeConfig.readXml(parser);
+        return ZenModeConfig.readXml(parser, logger);
     }
 }
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..0019b3e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -20,7 +20,9 @@
 import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
 import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
+import static android.app.Flags.FLAG_BACKUP_RESTORE_LOGGING;
 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 +63,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;
@@ -81,6 +84,8 @@
 import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
 import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
 import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG;
+import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES;
 import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL;
 import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
 
@@ -102,6 +107,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.notNull;
@@ -116,15 +122,17 @@
 import android.Manifest;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
 import android.app.Flags;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
+import android.app.backup.BackupRestoreEventLogger;
 import android.app.compat.CompatChanges;
 import android.content.ComponentName;
-import android.content.ContentResolver;
+import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -271,7 +279,6 @@
     private TestableLooper mTestableLooper;
     private final TestClock mTestClock = new TestClock();
     private ZenModeHelper mZenModeHelper;
-    private ContentResolver mContentResolver;
     @Mock
     DeviceEffectsApplier mDeviceEffectsApplier;
     @Mock
@@ -282,8 +289,7 @@
 
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
-        return FlagsParameterization.progressionOf(FLAG_MODES_API,
-                FLAG_MODES_UI);
+        return FlagsParameterization.allCombinationsOf(FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING);
     }
 
     public ZenModeHelperTest(FlagsParameterization flags) {
@@ -296,7 +302,6 @@
 
         mTestableLooper = TestableLooper.get(this);
         mContext.ensureTestableResources();
-        mContentResolver = mContext.getContentResolver();
         mResources = mock(Resources.class, withSettings()
                 .spiedInstance(mContext.getResources()));
         mPkg = mContext.getPackageName();
@@ -314,11 +319,16 @@
 
         mContext.addMockSystemService(AppOpsManager.class, mAppOps);
         mContext.addMockSystemService(NotificationManager.class, mNotificationManager);
+        mContext.addMockSystemService(Context.ALARM_SERVICE, mock(AlarmManager.class));
 
         mConditionProviders = new ConditionProviders(mContext, new UserProfiles(),
                 AppGlobals.getPackageManager());
-        mConditionProviders.addSystemProvider(new CountdownConditionProvider());
-        mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
+        CountdownConditionProvider countdown = spy(new CountdownConditionProvider());
+        ScheduleConditionProvider schedule = spy(new ScheduleConditionProvider());
+        doNothing().when(countdown).notifyConditions(any());
+        doNothing().when(schedule).notifyConditions(any());
+        mConditionProviders.addSystemProvider(countdown);
+        mConditionProviders.addSystemProvider(schedule);
         mZenModeEventLogger = new ZenModeEventLoggerFake(mPackageManager);
         mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(), mTestClock,
                 mConditionProviders, mTestFlagResolver, mZenModeEventLogger);
@@ -377,7 +387,7 @@
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
-        mZenModeHelper.writeXml(serializer, false, version, UserHandle.USER_ALL);
+        mZenModeHelper.writeXml(serializer, false, version, UserHandle.USER_ALL, null);
         serializer.endDocument();
         serializer.flush();
         mZenModeHelper.setConfig(new ZenModeConfig(), null, ORIGIN_INIT, "writing xml",
@@ -385,13 +395,14 @@
         return baos;
     }
 
-    private ByteArrayOutputStream writeXmlAndPurgeForUser(Integer version, int userId)
+    private ByteArrayOutputStream writeXmlAndPurgeForUser(Integer version, int userId,
+            boolean forBackup, BackupRestoreEventLogger logger)
             throws Exception {
         TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
-        mZenModeHelper.writeXml(serializer, true, version, userId);
+        mZenModeHelper.writeXml(serializer, forBackup, version, userId, logger);
         serializer.endDocument();
         serializer.flush();
         ZenModeConfig newConfig = new ZenModeConfig();
@@ -689,13 +700,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 +733,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 +765,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 +802,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 +819,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 +929,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 +954,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 +979,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 +995,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 +1021,7 @@
 
         // Turn manual zen mode on
         mZenModeHelper.setManualZenMode(
+                UserHandle.CURRENT,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                 null,
                 ORIGIN_APP,
@@ -1019,6 +1029,7 @@
                 "test",
                 CUSTOM_PKG_UID);
         mZenModeHelper.setManualZenMode(
+                UserHandle.CURRENT,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                 null,
                 ORIGIN_APP,
@@ -1042,19 +1053,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 +1077,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()) {
@@ -1085,7 +1102,7 @@
 
         ByteArrayOutputStream baos = writeXmlAndPurge(null);
         TypedXmlPullParser parser = getParserForByteStream(baos);
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertEquals("Config mismatch: current vs expected: "
                         + new ZenModeDiff.ConfigDiff(mZenModeHelper.mConfig, expected), expected,
@@ -1094,9 +1111,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
 
@@ -1161,7 +1178,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
         List<StatsEvent> events = new LinkedList<>();
         mZenModeHelper.pullRules(events);
 
@@ -1319,8 +1336,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 +1365,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);
@@ -1370,6 +1386,10 @@
 
     @Test
     public void testWriteXml_onlyBackupsTargetUser() throws Exception {
+        BackupRestoreEventLogger logger = null;
+        if (android.app.Flags.backupRestoreLogging()) {
+            logger = mock(BackupRestoreEventLogger.class);
+        }
         // Setup configs for user 10 and 11.
         setupZenConfig();
         ZenModeConfig config10 = mZenModeHelper.mConfig.copy();
@@ -1386,15 +1406,16 @@
                 SYSTEM_UID);
 
         // Backup user 10 and reset values.
-        ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, 10);
+        ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, 10, true, logger);
         ZenModeConfig newConfig11 = new ZenModeConfig();
         newConfig11.user = 11;
         mZenModeHelper.mConfigs.put(11, newConfig11);
 
         // Parse backup data.
         TypedXmlPullParser parser = getParserForByteStream(baos);
-        mZenModeHelper.readXml(parser, true, 10);
-        mZenModeHelper.readXml(parser, true, 11);
+        mZenModeHelper.readXml(parser, true, 10, logger);
+        parser = getParserForByteStream(baos);
+        mZenModeHelper.readXml(parser, true, 11, logger);
 
         ZenModeConfig actual = mZenModeHelper.mConfigs.get(10);
         if (Flags.modesUi()) {
@@ -1408,39 +1429,72 @@
                 "Config mismatch: current vs expected: "
                         + new ZenModeDiff.ConfigDiff(actual, config10), config10, actual);
         assertNotEquals("Expected config mismatch", config11, mZenModeHelper.mConfigs.get(11));
+
+        if (android.app.Flags.backupRestoreLogging()) {
+            verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_CONFIG, 1);
+            // If this is modes_ui, this is manual + single default rule
+            // If not modes_ui, it's two default automatic rules + manual policy
+            verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_RULES, Flags.modesUi() ? 2 : 3);
+            verify(logger, never())
+                    .logItemsBackupFailed(anyString(), anyInt(), anyString());
+
+            verify(logger, times(2)).logItemsRestored(DATA_TYPE_ZEN_RULES, Flags.modesUi() ? 2 : 3);
+            verify(logger, never())
+                    .logItemsRestoreFailed(anyString(), anyInt(), anyString());
+        }
     }
 
     @Test
     public void testReadXmlRestore_forSystemUser() throws Exception {
+        BackupRestoreEventLogger logger = null;
+        if (android.app.Flags.backupRestoreLogging()) {
+            logger = mock(BackupRestoreEventLogger.class);
+        }
         setupZenConfig();
         // one enabled automatic rule
         mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules();
         ZenModeConfig original = mZenModeHelper.mConfig.copy();
 
-        ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
+        ByteArrayOutputStream baos = writeXmlAndPurgeForUser(
+                null, UserHandle.USER_SYSTEM, true, logger);
         TypedXmlPullParser parser = getParserForByteStream(baos);
-        mZenModeHelper.readXml(parser, true, UserHandle.USER_SYSTEM);
+        mZenModeHelper.readXml(parser, true, UserHandle.USER_SYSTEM, logger);
 
         assertEquals("Config mismatch: current vs original: "
                         + new ZenModeDiff.ConfigDiff(mZenModeHelper.mConfig, original),
                 original, mZenModeHelper.mConfig);
         assertEquals(original.hashCode(), mZenModeHelper.mConfig.hashCode());
+
+        if (android.app.Flags.backupRestoreLogging()) {
+            verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_CONFIG, 1);
+            verify(logger).logItemsBackedUp(DATA_TYPE_ZEN_RULES, 2);
+            verify(logger, never())
+                    .logItemsBackupFailed(anyString(), anyInt(), anyString());
+            verify(logger).logItemsRestored(DATA_TYPE_ZEN_RULES, 2);
+            verify(logger, never())
+                    .logItemsRestoreFailed(anyString(), anyInt(), anyString());
+        }
     }
 
     /** Restore should ignore the data's user id and restore for the target user. */
     @Test
     public void testReadXmlRestore_forNonSystemUser() throws Exception {
+        BackupRestoreEventLogger logger = null;
+        if (android.app.Flags.backupRestoreLogging()) {
+            logger = mock(BackupRestoreEventLogger.class);
+        }
         // Setup config.
         setupZenConfig();
         mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules();
         ZenModeConfig expected = mZenModeHelper.mConfig.copy();
 
         // Backup data for user 0.
-        ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
+        ByteArrayOutputStream baos = writeXmlAndPurgeForUser(
+                null, UserHandle.USER_SYSTEM, true, logger);
 
         // Restore data for user 10.
         TypedXmlPullParser parser = getParserForByteStream(baos);
-        mZenModeHelper.readXml(parser, true, 10);
+        mZenModeHelper.readXml(parser, true, 10, logger);
 
         ZenModeConfig actual = mZenModeHelper.mConfigs.get(10);
         expected.user = 10;
@@ -1454,17 +1508,21 @@
 
     @Test
     public void testReadXmlRestore_doesNotEnableManualRule() throws Exception {
+        BackupRestoreEventLogger logger = null;
+        if (android.app.Flags.backupRestoreLogging()) {
+            logger = mock(BackupRestoreEventLogger.class);
+        }
         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();
 
         ByteArrayOutputStream baos = writeXmlAndPurge(null);
         TypedXmlPullParser parser = getParserForByteStream(baos);
-        mZenModeHelper.readXml(parser, true, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, true, UserHandle.USER_ALL, logger);
 
         ZenModeConfig result = mZenModeHelper.getConfig();
         assertThat(result.isManualActive()).isFalse();
@@ -1518,7 +1576,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
         ZenModeConfig.ZenRule current = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -1529,6 +1587,10 @@
 
     @Test
     public void testReadXmlRestoreWithZenPolicy_forSystemUser() throws Exception {
+        BackupRestoreEventLogger logger = null;
+        if (android.app.Flags.backupRestoreLogging()) {
+            logger = mock(BackupRestoreEventLogger.class);
+        }
         final String ruleId = "customRule";
         setupZenConfig();
 
@@ -1560,9 +1622,10 @@
             SystemZenRules.maybeUpgradeRules(mContext, expected);
         }
 
-        ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
+        ByteArrayOutputStream baos = writeXmlAndPurgeForUser(
+                null, UserHandle.USER_SYSTEM, true, logger);
         TypedXmlPullParser parser = getParserForByteStream(baos);
-        mZenModeHelper.readXml(parser, true, UserHandle.USER_SYSTEM);
+        mZenModeHelper.readXml(parser, true, UserHandle.USER_SYSTEM, logger);
 
         ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
         ZenModeConfig.ZenRule current = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -1574,7 +1637,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<>();
@@ -1594,10 +1657,10 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertTrue(mZenModeHelper.mConfig.automaticRules.containsKey("customRule"));
-        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy(UserHandle.CURRENT));
     }
 
     @Test
@@ -1614,7 +1677,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
 
@@ -1630,7 +1693,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
     }
@@ -1649,7 +1712,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
     }
@@ -1668,7 +1731,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertThat(mZenModeHelper.mConfig.getZenPolicy()
                 .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse();
@@ -1693,7 +1756,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertThat(mZenModeHelper.mConfig.getZenPolicy()
                 .isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse();
@@ -1712,7 +1775,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertThat(mZenModeHelper.mConfig.getZenPolicy()
                 .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse();
@@ -1727,7 +1790,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
@@ -1739,7 +1802,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // check default rules
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -1748,13 +1811,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
@@ -1775,7 +1838,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // check default rules
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -1785,14 +1848,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
@@ -1829,7 +1892,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // check default rules
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -1839,13 +1902,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<>();
@@ -1899,7 +1962,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // check default rules
         int expectedNumAutoRules = 1 + ZenModeConfig.getDefaultRuleIds().size(); // custom + default
@@ -1910,7 +1973,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 +1986,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<>();
@@ -1952,10 +2015,10 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // 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 +2035,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();
@@ -1991,10 +2054,10 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // 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 +2087,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();
@@ -2048,10 +2111,10 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // 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;
@@ -2114,7 +2177,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // check default rules
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -2166,7 +2229,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // Implicit rule was updated.
         assertThat(mZenModeHelper.mConfig.automaticRules.get(implicitRuleBeforeModesUi.id))
@@ -2204,7 +2267,7 @@
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         // Both rules were untouched
         assertThat(mZenModeHelper.mConfig.automaticRules.get(implicitRuleWithModesUi.id))
@@ -2345,8 +2408,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 +2419,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 +2440,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 +2451,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 +2472,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 +2483,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 +2499,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 +2520,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 +2546,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 +2561,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 +2607,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 +2627,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 +2637,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 +2653,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 +2674,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 +2698,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 +2723,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 +2754,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 +2788,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 +2816,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 +2825,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 +2837,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 +2872,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 +2900,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 +2915,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 +2931,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 +2941,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 +2958,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 +2968,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 +2978,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 +3004,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 +3024,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 +3045,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 +3065,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 +3085,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 +3109,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 +3147,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 +3191,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 +3212,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 +3287,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 +3310,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 +3378,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 +3406,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 +3494,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 +3554,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 +3564,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 +3576,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 +3680,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 +3690,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 +3777,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 +3846,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 +3857,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 +3898,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 +3940,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 +4028,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 +4064,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 +4102,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 +4146,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 +4185,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 +4208,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 +4245,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 +4268,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 +4310,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 +4331,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 +4362,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 +4375,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 +4421,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 +4457,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 +4487,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 +4505,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 +4547,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 +4567,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 +4604,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 +4624,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 +4654,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 +4672,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 +4687,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 +4696,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 +4728,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 +4750,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 +4760,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 +4776,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 +4787,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 +4808,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 +4838,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 +4869,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 +4881,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 +4906,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 +4923,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 +4941,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 +4958,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 +4977,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 +4993,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 +5017,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 +5035,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 +5061,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 +5079,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 +5105,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 +5133,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 +5157,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 +5180,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 +5208,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 +5232,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 +5257,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 +5277,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 +5301,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 +5327,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 +5341,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 +5364,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 +5380,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 +5404,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 +5425,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 +5466,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 +5480,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 +5537,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 +5571,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 +5582,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 +5622,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 +5633,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 +5664,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 +5685,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 +5708,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 +5732,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 +5755,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 +5768,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 +5786,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 +5803,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 +5853,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 +5960,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 +5969,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 +6000,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 +6018,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 +6039,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 +6050,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 +6061,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 +6080,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 +6099,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 +6115,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 +6139,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 +6171,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 +6199,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 +6217,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 +6228,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);
@@ -6166,14 +6249,57 @@
     }
 
     @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void applyGlobalZenModeAsImplicitZenRule_again_refreshesRuleName() {
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).name)
+                .isEqualTo("Do Not Disturb (" + CUSTOM_APP_LABEL + ")");
+        // "Break" the rule name to check that applying again restores it.
+        mZenModeHelper.mConfig.automaticRules.valueAt(0).name = "BOOM!";
+
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), CUSTOM_PKG_UID, ZEN_MODE_ALARMS);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).name)
+                .isEqualTo("Do Not Disturb (" + CUSTOM_APP_LABEL + ")");
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void applyGlobalZenModeAsImplicitZenRule_again_doesNotChangeCustomizedRuleName() {
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).name)
+                .isEqualTo("Do Not Disturb (" + CUSTOM_APP_LABEL + ")");
+        String ruleId = ZenModeConfig.implicitRuleId(mContext.getPackageName());
+
+        // User chooses a new name.
+        AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                new AutomaticZenRule.Builder(azr).setName("User chose this").build(),
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
+
+        // App triggers the rule again.
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), CUSTOM_PKG_UID, ZEN_MODE_ALARMS);
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).name)
+                .isEqualTo("User chose this");
+    }
+
+    @Test
     @DisableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_flagOff_ignored() {
         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 +6312,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 +6337,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 +6369,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 +6378,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 +6411,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 +6420,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()
@@ -6311,13 +6443,57 @@
     }
 
     @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void applyGlobalPolicyAsImplicitZenRule_again_refreshesRuleName() {
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), CUSTOM_PKG_UID, new Policy(0, 0, 0));
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).name)
+                .isEqualTo("Do Not Disturb (" + CUSTOM_APP_LABEL + ")");
+        // "Break" the rule name to check that updating it again restores it.
+        mZenModeHelper.mConfig.automaticRules.valueAt(0).name = "BOOM!";
+
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), CUSTOM_PKG_UID, new Policy(0, 0, 0));
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).name)
+                .isEqualTo("Do Not Disturb (" + CUSTOM_APP_LABEL + ")");
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void applyGlobalPolicyAsImplicitZenRule_again_doesNotChangeCustomizedRuleName() {
+        mZenModeHelper.mConfig.automaticRules.clear();
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), CUSTOM_PKG_UID, new Policy(0, 0, 0));
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).name)
+                .isEqualTo("Do Not Disturb (" + CUSTOM_APP_LABEL + ")");
+        String ruleId = ZenModeConfig.implicitRuleId(mContext.getPackageName());
+
+        // User chooses a new name.
+        AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                new AutomaticZenRule.Builder(azr).setName("User chose this").build(),
+                ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
+
+        // App updates the implicit rule again.
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(UserHandle.CURRENT,
+                mContext.getPackageName(), CUSTOM_PKG_UID, new Policy(0, 0, 0));
+
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).name)
+                .isEqualTo("User chose this");
+    }
+
+    @Test
     @DisableFlags(FLAG_MODES_API)
     public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() {
         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 +6505,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 +6519,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 +6538,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 +6575,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 +6604,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 +6620,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 +6631,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 +6653,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 +6668,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 +6677,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 +6691,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 +6706,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 +6721,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 +6736,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 +6750,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 +6768,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 +6782,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 +6791,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 +6805,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 +6828,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 +6837,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 +6851,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 +6862,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 +6877,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 +6914,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 +6960,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 +6984,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 +7015,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 +7092,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();
@@ -6925,15 +7109,17 @@
 
         // Now simulate a reboot -> reload the configuration after purging.
         TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         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 +7130,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();
@@ -6963,9 +7150,10 @@
 
         // Now simulate a reboot -> reload the configuration after purging.
         TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
-        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 +7166,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));
 
@@ -7001,7 +7189,7 @@
         ByteArrayOutputStream xmlBytes = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_UI);
         TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
 
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertThat(mZenModeHelper.mConfig.automaticRules).doesNotContainKey(
                 ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
@@ -7021,7 +7209,7 @@
         ByteArrayOutputStream xmlBytes = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_UI);
         TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
 
-        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         assertThat(mZenModeHelper.mConfig.automaticRules).containsKey(
                 ZenModeConfig.EVENTS_OBSOLETE_RULE_ID);
@@ -7039,15 +7227,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,
@@ -7100,9 +7335,10 @@
         rule.zenMode = zenMode;
         rule.zenPolicy = policy;
         rule.pkg = ownerPkg;
-        rule.name = CUSTOM_APP_LABEL;
-        if (!Flags.modesUi()) {
-            rule.iconResName = ICON_RES_NAME;
+        if (Flags.modesUi()) {
+            rule.name = mContext.getString(R.string.zen_mode_implicit_name, CUSTOM_APP_LABEL);
+        } else {
+            rule.name = CUSTOM_APP_LABEL;
         }
         rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description,
                 CUSTOM_APP_LABEL);
@@ -7122,7 +7358,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/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 8aa8a84..0933590 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -59,7 +59,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.PwlePoint;
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationConfig;
@@ -888,7 +888,7 @@
         fakeVibrator.setMinEnvelopeEffectControlPointDurationMillis(20);
 
         VibrationEffect effect = VibrationEffect.startWaveformEnvelope()
-                .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20)
+                .addControlPoint(/*amplitude=*/ 0.1f, /*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)
@@ -902,20 +902,47 @@
         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));
+                expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 0),
+                expectedPwle(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20),
+                expectedPwle(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
+                expectedPwle(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20),
+                expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30)
+        ), fakeVibrator.getEffectPwlePoints(vibration.id));
+
+    }
+
+    @Test
+    @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void vibrate_singleVibratorPwle_withInitialFrequency_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(/*initialFrequency=*/ 30)
+                .addControlPoint(/*amplitude=*/ 0.1f, /*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(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 30f, /*timeMillis=*/ 0),
+                expectedPwle(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20),
+                expectedPwle(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
+                expectedPwle(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20),
+                expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30)
+        ), fakeVibrator.getEffectPwlePoints(vibration.id));
 
     }
 
@@ -2013,10 +2040,8 @@
                 duration);
     }
 
-    private VibrationEffectSegment expectedPwle(float startAmplitude, float endAmplitude,
-            float startFrequencyHz, float endFrequencyHz, int duration) {
-        return new PwleSegment(startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz,
-                duration);
+    private PwlePoint expectedPwle(float amplitude, float frequencyHz, int timeMillis) {
+        return new PwlePoint(amplitude, frequencyHz, timeMillis);
     }
 
     private List<Float> expectedAmplitudes(int... amplitudes) {
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 bc8db3b..0978f48 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -44,7 +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.PwlePoint;
 import android.os.vibrator.RampSegment;
 
 import androidx.test.InstrumentationRegistry;
@@ -274,9 +274,9 @@
         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)
+        PwlePoint[] primitives = new PwlePoint[]{
+                new PwlePoint(/*amplitude=*/ 0, /*frequencyHz=*/ 100, /*timeMillis=*/ 0),
+                new PwlePoint(/*amplitude=*/ 1, /*frequencyHz=*/ 200, /*timeMillis=*/ 10)
         };
         assertEquals(15L, controller.on(primitives, 12));
         assertTrue(controller.isVibrating());
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 2c3e9b2..4dc59c2 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -26,7 +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.PwlePoint;
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
@@ -50,6 +50,7 @@
     private final Map<Long, PrebakedSegment> mEnabledAlwaysOnEffects = new HashMap<>();
     private final Map<Long, List<VibrationEffectSegment>> mEffectSegments = new TreeMap<>();
     private final Map<Long, List<VibrationEffect.VendorEffect>> mVendorEffects = new TreeMap<>();
+    private final Map<Long, List<PwlePoint>> mEffectPwlePoints = new TreeMap<>();
     private final Map<Long, List<Integer>> mBraking = new HashMap<>();
     private final List<Float> mAmplitudes = new ArrayList<>();
     private final List<Boolean> mExternalControlStates = new ArrayList<>();
@@ -91,6 +92,10 @@
         mVendorEffects.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(vendorEffect);
     }
 
+    void recordEffectPwlePoint(long vibrationId, PwlePoint pwlePoint) {
+        mEffectPwlePoints.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(pwlePoint);
+    }
+
     void recordBraking(long vibrationId, int braking) {
         mBraking.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(braking);
     }
@@ -195,11 +200,11 @@
         }
 
         @Override
-        public long composePwleV2(PwleSegment[] primitives, long vibrationId) {
+        public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId) {
             long duration = 0;
-            for (PwleSegment primitive: primitives) {
-                duration += primitive.getDuration();
-                recordEffectSegment(vibrationId, primitive);
+            for (PwlePoint pwlePoint: pwlePoints) {
+                duration += pwlePoint.getTimeMillis();
+                recordEffectPwlePoint(vibrationId, pwlePoint);
             }
             applyLatency(mOnLatency);
             scheduleListener(duration, vibrationId);
@@ -482,6 +487,28 @@
         return result;
     }
 
+    /** Return list of {@link PwlePoint} played by this controller, in order. */
+    public List<PwlePoint> getEffectPwlePoints(long vibrationId) {
+        if (mEffectPwlePoints.containsKey(vibrationId)) {
+            return new ArrayList<>(mEffectPwlePoints.get(vibrationId));
+        } else {
+            return new ArrayList<>();
+        }
+    }
+
+    /**
+     * Returns a list of all vibrations' {@link PwlePoint}s, for external-use where vibration
+     * IDs aren't exposed.
+     */
+    public List<PwlePoint> getAllEffectPwlePoints() {
+        // Returns segments in order of vibrationId, which increases over time. TreeMap gives order.
+        ArrayList<PwlePoint> result = new ArrayList<>();
+        for (List<PwlePoint> subList : mEffectPwlePoints.values()) {
+            result.addAll(subList);
+        }
+        return result;
+    }
+
     /** Return list of states set for external control to the fake vibrator hardware. */
     public List<Boolean> getExternalControlStates() {
         return mExternalControlStates;
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/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/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index 3742249..00c9691 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -143,6 +143,10 @@
         doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
     }
 
+    void setDisplayIgnoreActivitySizeRestrictions(boolean enabled) {
+        doReturn(enabled).when(mDisplayContent).isDisplayIgnoreActivitySizeRestrictions();
+    }
+
     void configureTaskBounds(@NonNull Rect taskBounds) {
         doReturn(taskBounds).when(mTaskStack.top()).getBounds();
     }
@@ -255,6 +259,10 @@
         doReturn(embedded).when(mActivityStack.top()).isEmbedded();
     }
 
+    void setTopActivityHasLetterboxedBounds(boolean letterboxed) {
+        doReturn(letterboxed).when(mActivityStack.top()).areBoundsLetterboxed();
+    }
+
     void setTopActivityVisible(boolean isVisible) {
         doReturn(isVisible).when(mActivityStack.top()).isVisible();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
index b839113..14ef913 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
@@ -17,9 +17,12 @@
 package com.android.server.wm;
 
 import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
 
@@ -29,10 +32,13 @@
 import static org.junit.Assert.assertNotEquals;
 
 import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.annotation.NonNull;
 
+import com.android.window.flags.Flags;
+
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
@@ -288,6 +294,93 @@
         });
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+    public void testHasFullscreenOverride_displayIgnoreActivitySizeRestrictionsTrue() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.setDisplayIgnoreActivitySizeRestrictions(true);
+                a.createActivityWithComponent();
+            });
+
+            robot.checkHasFullscreenOverride(true);
+        });
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+    public void testHasFullscreenOverride_displayIgnoreActivitySizeRestrictionsFalse() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.setDisplayIgnoreActivitySizeRestrictions(false);
+                a.createActivityWithComponent();
+            });
+
+            robot.checkHasFullscreenOverride(false);
+        });
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+    public void testPropFalse_displayIgnoreActivitySizeRestrictionsTrue_notOverridden() {
+        runTestScenario((robot) -> {
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+            robot.applyOnActivity((a) -> {
+                a.setDisplayIgnoreActivitySizeRestrictions(true);
+                a.createActivityWithComponent();
+            });
+
+            robot.checkHasFullscreenOverride(false);
+        });
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+    public void testPropTrue_displayIgnoreActivitySizeRestrictionsFalse_notOverridden() {
+        runTestScenario((robot) -> {
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
+            robot.applyOnActivity((a) -> {
+                a.setDisplayIgnoreActivitySizeRestrictions(false);
+                a.createActivityWithComponent();
+            });
+
+            robot.checkHasFullscreenOverride(false);
+        });
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+    public void testNotInSizeCompatMode_displayIgnoreActivitySizeRestrictionsTrue() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setDisplayIgnoreActivitySizeRestrictions(true);
+                a.configureTopActivity(/* minAspect */ -1f, /* maxAspect */-1f,
+                        SCREEN_ORIENTATION_LANDSCAPE, true);
+                a.rotateDisplayForTopActivity(ROTATION_90);
+
+                a.checkTopActivityInSizeCompatMode(false);
+            });
+        });
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_VDM_FORCE_APP_UNIVERSAL_RESIZABLE_API)
+    public void testInSizeCompatMode_displayIgnoreActivitySizeRestrictionsFalse() {
+        runTestScenario((robot) -> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(true);
+                a.setDisplayIgnoreActivitySizeRestrictions(false);
+                a.configureTopActivity(/* minAspect */ -1f, /* maxAspect */-1f,
+                        SCREEN_ORIENTATION_LANDSCAPE, true);
+                a.rotateDisplayForTopActivity(ROTATION_90);
+
+                a.checkTopActivityInSizeCompatMode(true);
+            });
+        });
+    }
+
     /**
      * Runs a test scenario providing a Robot.
      */
@@ -366,6 +459,11 @@
             }
         }
 
+        void checkHasFullscreenOverride(boolean expected) {
+            assertEquals(expected,
+                    getTopActivityAppCompatAspectRatioOverrides().hasFullscreenOverride());
+        }
+
         private AppCompatAspectRatioOverrides getTopActivityAppCompatAspectRatioOverrides() {
             return activity().top().mAppCompatController.getAppCompatAspectRatioOverrides();
         }
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 7bc9f30..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;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java
index 44b69f1..c9c31df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java
@@ -39,6 +39,7 @@
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.DisplayShape;
+import android.view.FrameRateCategoryRate;
 import android.view.RoundedCorner;
 import android.view.RoundedCorners;
 import android.view.SurfaceControl.RefreshRateRange;
@@ -235,6 +236,9 @@
         } else if (type.isArray() && type.getComponentType().equals(Display.Mode.class)) {
             field.set(first, new Display.Mode[]{new Display.Mode(100, 200, 300)});
             field.set(second, new Display.Mode[]{new Display.Mode(10, 20, 30)});
+        } else if (type.equals(FrameRateCategoryRate.class)) {
+            field.set(first, new FrameRateCategoryRate(16666667, 11111111));
+            field.set(second, new FrameRateCategoryRate(11111111, 8333333));
         } else {
             throw new IllegalArgumentException("Field " + field
                     + " is not supported by this test, please add implementation of setting "
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/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
index 5187f87..42752c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
@@ -217,6 +217,19 @@
         });
     }
 
+    @Test
+    public void testNotApplyStrategyToTranslucentActivitiesOverNotLetterboxedActivities() {
+        runTestScenario((robot) -> {
+            robot.transparentActivity((ta) -> {
+                ta.activity().setTopActivityHasLetterboxedBounds(false);
+                ta.launchTransparentActivityInTask();
+
+                ta.checkTopActivityTransparentPolicyStartNotInvoked();
+                ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
+            });
+        });
+    }
+
     @EnableFlags(com.android.window.flags.Flags.FLAG_RESPECT_NON_TOP_VISIBLE_FIXED_ORIENTATION)
     @Test
     public void testNotRunStrategyToTranslucentActivitiesIfRespectOrientation() {
@@ -388,6 +401,7 @@
             mTransparentActivityRobot = new AppCompatTransparentActivityRobot(activity());
             // We always create at least an opaque activity in a Task
             activity().createNewTaskWithBaseActivity();
+            activity().setTopActivityHasLetterboxedBounds(true);
         }
 
         @Override
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/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 4ccbc32..66d75f7 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -907,29 +907,6 @@
     }
 
     /**
-     * Ensure the caller (or self, if not processing an IPC) has
-     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
-     * {@link android.Manifest.permission#READ_PHONE_NUMBERS}.
-     *
-     * @throws SecurityException if the caller does not have the required permission/privileges
-     */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.READ_PHONE_NUMBERS,
-            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
-    })
-    public static boolean checkCallingOrSelfReadPrivilegedPhoneStatePermissionOrReadPhoneNumber(
-            Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
-            String message) {
-        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-            return false;
-        }
-        return (context.checkCallingOrSelfPermission(
-                Manifest.permission.READ_PRIVILEGED_PHONE_STATE) == PERMISSION_GRANTED
-                || checkCallingOrSelfReadPhoneNumber(context, subId, callingPackage,
-                callingFeatureId, message));
-    }
-
-    /**
      * @return true if the specified {@code uid} is for a system or phone process, no matter if runs
      * as system user or not.
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6535b9b..0808cc3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -11415,6 +11415,7 @@
         sDefaults.putIntArray(KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY, new int[]{1, 2, 3});
         sDefaults.putInt(KEY_WEAR_CONNECTIVITY_BT_TO_CELL_DELAY_MS_INT, -1);
         sDefaults.putInt(KEY_WEAR_CONNECTIVITY_EXTEND_BT_TO_CELL_DELAY_ON_WIFI_MS_INT, -1);
+        sDefaults.putInt(KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE, 255);
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fad59f8b..6f2c862 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -133,6 +133,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
 
@@ -6282,14 +6283,17 @@
      * The contents of the file is a <b>Ip Multimedia Service Private User Identity</b> of the user
      * as defined in the section 4.2.2 of 3GPP TS 131 103.
      *
-     * @return IMPI (IMS private user identity) of type string.
+     * @return IMPI (IMS private user identity) of type string or null if the IMPI isn't present
+     *         on the ISIM.
      * @throws IllegalStateException in case the ISIM has’t been loaded
      * @throws SecurityException if the caller does not have the required permission/privileges
      * @hide
      */
-    @NonNull
+    @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    @SystemApi
     @RequiresPermission(android.Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @Nullable
     public String getImsPrivateUserIdentity() {
         try {
             IPhoneSubInfo info = getSubscriberInfoService();
@@ -6370,6 +6374,9 @@
      * The contents of the file are <b>Ip Multimedia Service Public User Identities</b> of the user
      * as defined in the section 4.2.4 of 3GPP TS 131 103. It contains one or more records.
      *
+     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission or carrier
+     * privileges.
+     *
      * @return List of public user identities of type android.net.Uri or empty list  if
      *         EF_IMPU is not available.
      * @throws IllegalStateException in case the ISIM hasn’t been loaded
@@ -6378,18 +6385,18 @@
      *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
-    @NonNull
-    @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_NUMBERS,
-            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+    @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    @SystemApi
+    @RequiresPermission(value = Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional = true)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @NonNull
     public List<Uri> getImsPublicUserIdentities() {
         try {
             IPhoneSubInfo info = getSubscriberInfoService();
             if (info == null) {
                 throw new RuntimeException("IMPU error: Subscriber Info is null");
             }
-            return info.getImsPublicUserIdentities(getSubId(), getOpPackageName(),
-                    getAttributionTag());
+            return info.getImsPublicUserIdentities(getSubId(), getOpPackageName());
         } catch (IllegalArgumentException | NullPointerException ex) {
             Rlog.e(TAG, "getImsPublicUserIdentities Exception = " + ex);
         } catch (RemoteException ex) {
@@ -8684,7 +8691,10 @@
      * @return an array of PCSCF strings with one PCSCF per string, or null if
      *         not present or not loaded
      * @hide
+     * @deprecated use {@link #getImsPcscfAddresses()} instead.
      */
+    @Deprecated
+    @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
     @UnsupportedAppUsage
     public String[] getIsimPcscf() {
         try {
@@ -8701,6 +8711,40 @@
         }
     }
 
+    /**
+     * Returns the IMS Proxy Call Session Control Function(P-CSCF) that were loaded from the ISIM.
+     *
+     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission or carrier
+     * privileges.
+     *
+     * @return List of P-CSCF address strings or empty list if not available.
+     * @throws IllegalStateException in case the ISIM hasn’t been loaded
+     * @throws SecurityException if the caller does not have the required permission/privilege
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    @SystemApi
+    @RequiresPermission(value = Manifest.permission.READ_PRIVILEGED_PHONE_STATE, conditional = true)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    @NonNull
+    public List<String> getImsPcscfAddresses() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfoService();
+            if (info == null) {
+                throw new RuntimeException("P-CSCF error: Subscriber Info is null");
+            }
+            return info.getImsPcscfAddresses(getSubId(), getOpPackageName());
+        } catch (IllegalArgumentException | NullPointerException ex) {
+            Rlog.e(TAG, "getImsPcscfAddresses Exception = " + ex);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getImsPcscfAddresses Exception = " + ex);
+            ex.rethrowAsRuntimeException();
+        }
+        return Collections.EMPTY_LIST;
+    }
+
     /** UICC application type is unknown or not specified */
     public static final int APPTYPE_UNKNOWN = PhoneConstants.APPTYPE_UNKNOWN;
     /** UICC application type is SIM */
@@ -8934,8 +8978,10 @@
      * @throws UnsupportedOperationException If the device does not have
      *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
+     * @deprecated Use {@link #getSimServiceTable(int, Executor, OutcomeReceiver)} instead.
      */
-
+    @Deprecated
+    @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
     @Nullable
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -8963,6 +9009,55 @@
     }
 
     /**
+     * Fetches the sim service table from the EFUST/EFIST based on the application type
+     * {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}.
+     * The USIM service table EF is described in as per Section 4.2.8 of 3GPP TS 31.102.
+     * The ISIM service table EF is described in as per Section 4.2.7 of 3GPP TS 31.103.
+     *
+     * @param appType of type int of either {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}.
+     * @param executor executor to run the callback on.
+     * @param callback callback object to which the result will be delivered.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
+    public void getSimServiceTable(@UiccAppType int appType,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<byte[], Exception> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        IPhoneSubInfo info = getSubscriberInfoService();
+        if (info == null) {
+            executor.execute(() -> callback.onError(
+                    new RuntimeException("getSimServiceTable: Subscriber Info is null")));
+            return;
+        }
+
+        try {
+            String serviceTable;
+            if (appType == APPTYPE_ISIM) {
+                serviceTable  = info.getIsimIst(getSubId());
+            } else if ((appType == APPTYPE_USIM)) {
+                serviceTable = info.getSimServiceTable(getSubId(), APPTYPE_USIM);
+            } else {
+                serviceTable = null;
+            }
+
+            if (serviceTable == null) {
+                executor.execute(() -> callback.onResult(new byte[0]));
+            } else {
+                byte[] simServiceTable = IccUtils.hexStringToBytes(serviceTable);
+                executor.execute(() -> callback.onResult(simServiceTable));
+            }
+        } catch (Exception ex) {
+            executor.execute(() -> callback.onError(ex));
+        }
+    }
+
+    /**
      * Resets the {@link android.telephony.ims.ImsService} associated with the specified sim slot.
      * Used by diagnostic apps to force the IMS stack to be disabled and re-enabled in an effort to
      * recover from scenarios where the {@link android.telephony.ims.ImsService} gets in to a bad
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index 5946535..00d4bd0 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -16,6 +16,7 @@
 
 package android.telephony.ims;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,6 +32,7 @@
 import android.util.Log;
 
 import com.android.ims.internal.IImsCallSession;
+import com.android.internal.telephony.flags.Flags;
 
 import java.util.ArrayList;
 import java.util.Objects;
@@ -802,8 +804,8 @@
 
     /**
      * Notifies the result of transfer request.
-     * @hide
      */
+    @FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
     public void callSessionTransferred() {
         try {
             mListener.callSessionTransferred();
@@ -813,13 +815,13 @@
     }
 
     /**
-     * Notifies the result of transfer request.
+     * Notifies the result of the transfer request failure.
      *
      * @param reasonInfo {@link ImsReasonInfo} containing a reason for the
      * session transfer failure
-     * @hide
      */
-    public void callSessionTransferFailed(ImsReasonInfo reasonInfo) {
+    @FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    public void callSessionTransferFailed(@NonNull ImsReasonInfo reasonInfo) {
         try {
             mListener.callSessionTransferFailed(reasonInfo);
         } catch (RemoteException e) {
@@ -839,8 +841,8 @@
      * @param bitsPerSecond This value is the bitrate requested by the other party UE through
      *        RTP CMR, RTCPAPP or TMMBR, and ImsStack converts this value to the MAC bitrate
      *        (defined in TS36.321, range: 0 ~ 8000 kbit/s).
-     * @hide
      */
+    @FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
     public final void callSessionSendAnbrQuery(@MediaStreamType int mediaType,
                 @MediaStreamDirection int direction, @IntRange(from = 0) int bitsPerSecond) {
         Log.d(TAG, "callSessionSendAnbrQuery in imscallsessonListener");
diff --git a/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java b/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java
index 88d9aae..81cddb7 100644
--- a/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java
+++ b/telephony/java/android/telephony/ims/feature/ConnectionFailureInfo.java
@@ -16,12 +16,16 @@
 
 package android.telephony.ims.feature;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
 
+import com.android.internal.telephony.flags.Flags;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -30,6 +34,8 @@
  *
  * @hide
  */
+@FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+@SystemApi
 public final class ConnectionFailureInfo implements Parcelable {
 
     /** @hide */
@@ -67,7 +73,7 @@
     public static final int REASON_RRC_TIMEOUT = 6;
     /** Device currently not in service */
     public static final int REASON_NO_SERVICE = 7;
-    /** The PDN is no more active */
+    /** The PDN is no longer active */
     public static final int REASON_PDN_NOT_AVAILABLE = 8;
     /** Radio resource is busy with another subscription */
     public static final int REASON_RF_BUSY = 9;
@@ -135,6 +141,8 @@
 
     /**
      * @return the cause code from the network or modem specific to the failure.
+     *         See 3GPP TS 24.401 Annex A (Cause values for EPS mobility management) and
+     *         3GPP TS 24.501 Annex A (Cause values for 5GS mobility management).
      */
     public int getCauseCode() {
         return mCauseCode;
diff --git a/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java b/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java
index 245ee15..0029d49 100644
--- a/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java
+++ b/telephony/java/android/telephony/ims/feature/ImsTrafficSessionCallback.java
@@ -16,20 +16,26 @@
 
 package android.telephony.ims.feature;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.telephony.flags.Flags;
 
 /**
  * A callback class used to receive the result of {@link MmTelFeature#startImsTrafficSession}.
  * @hide
  */
+@FlaggedApi(Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+@SystemApi
 public interface ImsTrafficSessionCallback {
 
     /** The modem is ready to process the IMS traffic. */
     void onReady();
 
     /**
-     * Notifies that any IMS traffic is not sent to network due to any failure
-     * on cellular networks. IMS service shall call {@link MmTelFeature#stopImsTrafficSession()}
+     * Notifies that any IMS traffic can not be sent to the network due to the provided cellular
+     * network failure. IMS service shall call {@link MmTelFeature#stopImsTrafficSession()}
      * when receiving this callback.
      *
      * @param info The information of the failure.
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 3f02ae9..c6b11d7 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -16,6 +16,8 @@
 
 package android.telephony.ims.feature;
 
+import static com.android.internal.telephony.flags.Flags.FLAG_SUPPORT_IMS_MMTEL_INTERFACE;
+
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
@@ -964,6 +966,8 @@
      *
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int EPS_FALLBACK_REASON_NO_NETWORK_TRIGGER = 1;
 
     /**
@@ -976,6 +980,8 @@
      *
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int EPS_FALLBACK_REASON_NO_NETWORK_RESPONSE = 2;
 
     /** @hide */
@@ -1003,36 +1009,50 @@
      * Emergency call
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int IMS_TRAFFIC_TYPE_EMERGENCY = 0;
     /**
      * Emergency SMS
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int IMS_TRAFFIC_TYPE_EMERGENCY_SMS = 1;
     /**
      * Voice call
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int IMS_TRAFFIC_TYPE_VOICE = 2;
     /**
      * Video call
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int IMS_TRAFFIC_TYPE_VIDEO = 3;
     /**
      * SMS over IMS
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int IMS_TRAFFIC_TYPE_SMS = 4;
     /**
      * IMS registration and subscription for reg event package (signaling)
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int IMS_TRAFFIC_TYPE_REGISTRATION = 5;
     /**
      * Ut/XCAP (XML Configuration Access Protocol)
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int IMS_TRAFFIC_TYPE_UT_XCAP = 6;
 
     /** @hide */
@@ -1046,11 +1066,15 @@
      * Indicates that the traffic is an incoming traffic.
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int IMS_TRAFFIC_DIRECTION_INCOMING = 0;
     /**
      * Indicates that the traffic is an outgoing traffic.
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public static final int IMS_TRAFFIC_DIRECTION_OUTGOING = 1;
 
     private IImsMmTelListener mListener;
@@ -1291,9 +1315,11 @@
     /**
      * Triggers the EPS fallback procedure.
      *
-     * @param reason specifies the reason that causes EPS fallback.
+     * @param reason specifies the reason that EPS fallback was triggered.
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public final void triggerEpsFallback(@EpsFallbackReason int reason) {
         IImsMmTelListener listener = getListener();
         if (listener == null) {
@@ -1344,6 +1370,8 @@
      *
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public final void startImsTrafficSession(@ImsTrafficType int trafficType,
             @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
             @ImsTrafficDirection int trafficDirection,
@@ -1387,6 +1415,8 @@
      *
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public final void modifyImsTrafficSession(
             @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
             @NonNull ImsTrafficSessionCallback callback) {
@@ -1417,6 +1447,8 @@
      *
      * @hide
      */
+    @FlaggedApi(FLAG_SUPPORT_IMS_MMTEL_INTERFACE)
+    @SystemApi
     public final void stopImsTrafficSession(@NonNull ImsTrafficSessionCallback callback) {
         IImsMmTelListener listener = getListener();
         if (listener == null) {
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index f0c3504..be02232 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -26,6 +26,7 @@
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.SystemService;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -39,6 +40,7 @@
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyRegistryManager;
 
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.ITelephony;
@@ -61,14 +63,19 @@
 import java.util.stream.Collectors;
 
 /**
- * Manages satellite operations such as provisioning, pointing, messaging, location sharing, etc.
- * To get the object, call {@link Context#getSystemService(String)}.
+ * Manages satellite states such as monitoring enabled state and operations such as provisioning,
+ * pointing, messaging, location sharing, etc.
  *
- * @hide
+ * <p>To get the object, call {@link Context#getSystemService(String)} with
+ * {@link Context#SATELLITE_SERVICE}.
+ *
+ * <p>SatelliteManager is intended for use on devices with feature
+ * {@link PackageManager#FEATURE_TELEPHONY_SATELLITE}. On devices without the feature, the behavior
+ * is not reliable.
  */
+@SystemService(Context.SATELLITE_SERVICE)
+@FlaggedApi(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)
 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SATELLITE)
-@SystemApi
-@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
 public final class SatelliteManager {
     private static final String TAG = "SatelliteManager";
 
@@ -104,6 +111,8 @@
      */
     @Nullable private final Context mContext;
 
+    private TelephonyRegistryManager mTelephonyRegistryMgr;
+
     /**
      * Create an instance of the SatelliteManager.
      *
@@ -710,6 +719,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
@@ -727,6 +746,65 @@
             "android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT";
 
     /**
+     * Registers a {@link SatelliteStateChangeListener} to receive callbacks when the satellite
+     * state may have changed.
+     *
+     * <p>The callback method is immediately triggered with latest state on invoking this method if
+     * the state change has been notified before.
+     *
+     * @param executor The {@link Executor} where the {@code listener} will be invoked
+     * @param listener The listener to monitor the satellite state change
+     *
+     * @see SatelliteStateChangeListener
+     * @see TelephonyManager#hasCarrierPrivileges()
+     */
+    @FlaggedApi(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)
+    @RequiresPermission(anyOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE,
+            "carrier privileges"})
+    public void registerStateChangeListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteStateChangeListener listener) {
+        if (mContext == null) {
+            throw new IllegalStateException("Telephony service is null");
+        }
+
+        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+        if (mTelephonyRegistryMgr == null) {
+            throw new IllegalStateException("Telephony registry service is null");
+        }
+        mTelephonyRegistryMgr.addSatelliteStateChangeListener(executor, listener);
+    }
+
+    /**
+     * Unregisters the {@link SatelliteStateChangeListener} previously registered with
+     * {@link #registerStateChangeListener(Executor, SatelliteStateChangeListener)}.
+     *
+     * <p>It will be a no-op if the {@code listener} is not currently registered.
+     *
+     * @param listener The listener to unregister
+     *
+     * @see SatelliteStateChangeListener
+     * @see TelephonyManager#hasCarrierPrivileges()
+     */
+    @FlaggedApi(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)
+    @RequiresPermission(anyOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE,
+            "carrier privileges"})
+    public void unregisterStateChangeListener(@NonNull SatelliteStateChangeListener listener) {
+        if (mContext == null) {
+            throw new IllegalStateException("Telephony service is null");
+        }
+
+        mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+        if (mTelephonyRegistryMgr == null) {
+            throw new IllegalStateException("Telephony registry service is null");
+        }
+        mTelephonyRegistryMgr.removeSatelliteStateChangeListener(listener);
+    }
+
+    /**
      * Request to enable or disable the satellite modem and demo mode.
      * If satellite modem and cellular modem cannot work concurrently,
      * then this will disable the cellular modem if satellite modem is enabled,
diff --git a/telephony/java/android/telephony/satellite/SatelliteStateChangeListener.java b/telephony/java/android/telephony/satellite/SatelliteStateChangeListener.java
new file mode 100644
index 0000000..3aa910d
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteStateChangeListener.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A listener interface to monitor satellite state change events.
+ *
+ * <p>Call
+ * {@link SatelliteManager#registerStateChangeListener(Executor, SatelliteStateChangeListener)}
+ * to monitor. Call
+ * {@link SatelliteManager#unregisterStateChangeListener(SatelliteStateChangeListener)} to cancel.
+ *
+ * @see SatelliteManager#registerStateChangeListener(Executor, SatelliteStateChangeListener)
+ * @see SatelliteManager#unregisterStateChangeListener(SatelliteStateChangeListener)
+ */
+@FlaggedApi(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)
+public interface SatelliteStateChangeListener {
+    /**
+     * Called when satellite modem enabled state may have changed.
+     *
+     * <p>Note:there is no guarantee that this callback will only be invoked upon a change of state.
+     * In other word, in some cases, the callback may report with the same enabled states. It is the
+     * caller's responsibility to filter uninterested states.
+     *
+     * <p>Note:satellite enabled state is a device state that is NOT associated with subscription or
+     * SIM slot.
+     *
+     * @param isEnabled {@code true} means satellite modem is enabled.
+     */
+    void onEnabledStateChanged(boolean isEnabled);
+}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index b4d93fd..974cc14 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -208,10 +208,9 @@
     /**
      * Fetches the ISIM public user identities (EF_IMPU) from UICC based on subId
      */
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" +
-    "anyOf={android.Manifest.permission.READ_PHONE_NUMBERS, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})")
-     List<Uri> getImsPublicUserIdentities(int subId, String callingPackage,
-                                           String callingFeatureId);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
+     List<Uri> getImsPublicUserIdentities(int subId, String callingPackage);
 
     /**
      * Returns the IMS Service Table (IST) that was loaded from the ISIM.
@@ -227,6 +226,20 @@
     String[] getIsimPcscf(int subId);
 
     /**
+      * Fetches IMS Proxy Call Session Control Function(P-CSCF) based on the subscription.
+      *
+      * @param subId subscriptionId
+      * @param callingPackage package name of the caller
+      * @return List of IMS Proxy Call Session Control Function strings.
+      * @throws IllegalArgumentException if the subscriptionId is not valid
+      * @throws IllegalStateException in case the ISIM hasn’t been loaded.
+      * @throws SecurityException if the caller does not have the required permission
+      */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)")
+    List<String> getImsPcscfAddresses(int subId, String callingPackage);
+
+    /**
      * Returns the response of the SIM application on the UICC to authentication
      * challenge/response algorithm. The data string and challenge response are
      * Base64 encoded Strings.
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 6bf7ff5..3247d1f 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -53,6 +53,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -607,6 +608,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public List<IntentFilter> getRegisteredIntentFilters(BroadcastReceiver receiver) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public ComponentName startService(Intent service) {
         throw new UnsupportedOperationException();
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/InputShellCommandTest.java b/tests/Input/src/com/android/server/input/InputShellCommandTest.java
index 11f4633..a236244 100644
--- a/tests/Input/src/com/android/server/input/InputShellCommandTest.java
+++ b/tests/Input/src/com/android/server/input/InputShellCommandTest.java
@@ -133,6 +133,21 @@
         assertThat(mInputEventInjector.mInjectedEvents).isEmpty();
     }
 
+    @Test
+    public void testSwipeCommandEventFrequency() {
+        int[] durations = {100, 300, 500};
+        for (int durationMillis: durations) {
+            mInputEventInjector.mInjectedEvents.clear();
+            runCommand(String.format("swipe 200 800 200 200 %d", durationMillis));
+
+            // Add 2 events for ACTION_DOWN and ACTION_UP.
+            final int maxEventNum =
+                    (int) Math.ceil(InputShellCommand.SWIPE_EVENT_HZ_DEFAULT
+                            * (float) durationMillis / 1000) + 2;
+            assertThat(mInputEventInjector.mInjectedEvents.size()).isAtMost(maxEventNum);
+        }
+    }
+
     private InputEvent getSingleInjectedInputEvent() {
         assertThat(mInputEventInjector.mInjectedEvents).hasSize(1);
         return mInputEventInjector.mInjectedEvents.get(0);
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 787ae06..0b147d6 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -22,8 +22,10 @@
 import android.content.res.Resources
 import android.hardware.input.IInputManager
 import android.hardware.input.AidlKeyGestureEvent
+import android.hardware.input.AppLaunchData
 import android.hardware.input.IKeyGestureEventListener
 import android.hardware.input.IKeyGestureHandler
+import android.hardware.input.InputGestureData
 import android.hardware.input.InputManager
 import android.hardware.input.InputManagerGlobal
 import android.hardware.input.KeyGestureEvent
@@ -232,7 +234,7 @@
         keyGestureController.handleKeyGesture(/* deviceId = */ 0, intArrayOf(KeyEvent.KEYCODE_HOME),
             /* modifierState = */ 0, KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
             KeyGestureEvent.ACTION_GESTURE_COMPLETE, /* displayId */ 0,
-            /* focusedToken = */ null, /* flags = */ 0
+            /* focusedToken = */ null, /* flags = */ 0, /* appLaunchData = */null
         )
 
         assertEquals(
@@ -259,6 +261,7 @@
         val expectedKeys: IntArray,
         val expectedModifierState: Int,
         val expectedActions: IntArray,
+        val expectedAppLaunchData: AppLaunchData? = null,
     ) {
         override fun toString(): String = name
     }
@@ -799,6 +802,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)
@@ -820,6 +887,86 @@
     }
 
     @Test
+    @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
+    fun testSnapLeftFreeformTask() {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "ALT + [ -> Resizes a task to fit the left half of the screen",
+                intArrayOf(
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_LEFT_BRACKET
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+                intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET),
+                KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            )
+        )
+    }
+
+    @Test
+    @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
+    fun testSnapRightFreeformTask() {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "ALT + ] -> Resizes a task to fit the right half of the screen",
+                intArrayOf(
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_RIGHT_BRACKET
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+                intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET),
+                KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            )
+        )
+    }
+
+    @Test
+    @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
+    fun testMaximizeFreeformTask() {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "ALT + '=' -> Maximizes a task to fit the screen",
+                intArrayOf(
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_EQUALS
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+                intArrayOf(KeyEvent.KEYCODE_EQUALS),
+                KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            )
+        )
+    }
+
+    @Test
+    @EnableFlags(com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS)
+    fun testRestoreFreeformTask() {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        testKeyGestureInternal(
+            keyGestureController,
+            TestData(
+                "ALT + '-' -> Restores a task size to its previous bounds",
+                intArrayOf(
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_MINUS
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE,
+                intArrayOf(KeyEvent.KEYCODE_MINUS),
+                KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            )
+        )
+    }
+
+    @Test
     fun testCapsLockPressNotified() {
         val keyGestureController = KeyGestureController(context, testLooper.looper)
         val listener = KeyGestureEventListener()
@@ -911,6 +1058,62 @@
         testKeyGestureInternal(keyGestureController, test)
     }
 
+    @Keep
+    private fun customInputGesturesTestArguments(): Array<TestData> {
+        return arrayOf(
+            TestData(
+                "META + ALT + Q -> Go Home",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_Q
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+                intArrayOf(KeyEvent.KEYCODE_Q),
+                KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                )
+            ),
+            TestData(
+                "META + ALT + Q -> Launch app",
+                intArrayOf(
+                    KeyEvent.KEYCODE_CTRL_LEFT,
+                    KeyEvent.KEYCODE_SHIFT_LEFT,
+                    KeyEvent.KEYCODE_Q
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+                intArrayOf(KeyEvent.KEYCODE_Q),
+                KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON,
+                intArrayOf(
+                    KeyGestureEvent.ACTION_GESTURE_COMPLETE
+                ),
+                AppLaunchData.createLaunchDataForComponent("com.test", "com.test.BookmarkTest")
+            ),
+        )
+    }
+
+    @Test
+    @Parameters(method = "customInputGesturesTestArguments")
+    fun testCustomKeyGestures(test: TestData) {
+        val keyGestureController = KeyGestureController(context, testLooper.looper)
+        val builder = InputGestureData.Builder()
+            .setKeyGestureType(test.expectedKeyGestureType)
+            .setTrigger(
+                InputGestureData.createKeyTrigger(
+                    test.expectedKeys[0],
+                    test.expectedModifierState
+                )
+            );
+        if (test.expectedAppLaunchData != null) {
+            builder.setAppLaunchData(test.expectedAppLaunchData)
+        }
+        val inputGestureData = builder.build();
+
+        keyGestureController.addCustomInputGesture(0, inputGestureData.aidlData)
+        testKeyGestureInternal(keyGestureController, test)
+    }
+
     private fun testKeyGestureInternal(keyGestureController: KeyGestureController, test: TestData) {
         var handleEvents = mutableListOf<KeyGestureEvent>()
         val handler = KeyGestureHandler { event, _ ->
@@ -949,6 +1152,11 @@
                 test.expectedActions[i],
                 event.action
             )
+            assertEquals(
+                "Test: $test doesn't produce correct app launch data",
+                test.expectedAppLaunchData,
+                event.appLaunchData
+            )
         }
 
         keyGestureController.unregisterKeyGestureHandler(handler, 0)
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/graphics/HwAccelerationTest/AndroidManifest.xml b/tests/graphics/HwAccelerationTest/AndroidManifest.xml
index db3a992..05b2f4c 100644
--- a/tests/graphics/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/graphics/HwAccelerationTest/AndroidManifest.xml
@@ -24,7 +24,7 @@
     <uses-feature android:name="android.hardware.camera"/>
     <uses-feature android:name="android.hardware.camera.autofocus"/>
 
-    <uses-sdk android:minSdkVersion="21"/>
+    <uses-sdk android:minSdkVersion="21" />
 
     <application android:label="HwUi"
          android:theme="@android:style/Theme.Material.Light">
@@ -409,6 +409,24 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="ScrollingZAboveSurfaceView"
+            android:label="SurfaceView/Z-Above scrolling"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="ScrollingZAboveScaledSurfaceView"
+            android:label="SurfaceView/Z-Above scrolling, scaled surface"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name="StretchySurfaceViewActivity"
                   android:label="SurfaceView/Stretchy Movement"
                   android:exported="true">
diff --git a/tests/graphics/HwAccelerationTest/res/layout/scrolling_zabove_surfaceview.xml b/tests/graphics/HwAccelerationTest/res/layout/scrolling_zabove_surfaceview.xml
new file mode 100644
index 0000000..31e5774
--- /dev/null
+++ b/tests/graphics/HwAccelerationTest/res/layout/scrolling_zabove_surfaceview.xml
@@ -0,0 +1,131 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:context=".MainActivity">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="Above the ScrollView"
+        android:textColor="#FFFFFFFF"
+        android:background="#FF444444"
+        android:padding="32dp" />
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Header"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+            <SurfaceView
+                android:layout_width="match_parent"
+                android:layout_height="500dp"
+                android:id="@+id/surfaceview" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Item"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Item"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Item"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Item"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Item"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Item"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Item"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Item"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Scrolling Item"
+                android:background="#FFCCCCCC"
+                android:padding="32dp" />
+
+        </LinearLayout>
+
+    </ScrollView>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="Below the ScrollView"
+        android:textColor="#FFFFFFFF"
+        android:background="#FF444444"
+        android:padding="32dp" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveScaledSurfaceView.kt b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveScaledSurfaceView.kt
new file mode 100644
index 0000000..59ae885
--- /dev/null
+++ b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveScaledSurfaceView.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.test.hwui
+
+import android.app.Activity
+import android.graphics.Color
+import android.graphics.Paint
+import android.os.Bundle
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+
+class ScrollingZAboveScaledSurfaceView : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.scrolling_zabove_surfaceview)
+
+        findViewById<SurfaceView>(R.id.surfaceview).apply {
+            setZOrderOnTop(true)
+            holder.setFixedSize(1000, 2000)
+            holder.addCallback(object : SurfaceHolder.Callback {
+                override fun surfaceCreated(p0: SurfaceHolder) {
+
+                }
+
+                override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+                    holder.unlockCanvasAndPost(holder.lockCanvas().apply {
+                        drawColor(Color.BLUE)
+                        val paint = Paint()
+                        paint.textSize = 16 * resources.displayMetrics.density
+                        paint.textAlign = Paint.Align.CENTER
+                        paint.color = Color.WHITE
+                        drawText("I'm a setZOrderOnTop(true) SurfaceView!",
+                            (width / 2).toFloat(), (height / 2).toFloat(), paint)
+                    })
+                }
+
+                override fun surfaceDestroyed(p0: SurfaceHolder) {
+
+                }
+
+            })
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveSurfaceView.kt b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveSurfaceView.kt
new file mode 100644
index 0000000..ccb71ec
--- /dev/null
+++ b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingZAboveSurfaceView.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.test.hwui
+
+import android.app.Activity
+import android.graphics.Color
+import android.graphics.Paint
+import android.os.Bundle
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+
+class ScrollingZAboveSurfaceView : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.scrolling_zabove_surfaceview)
+
+        findViewById<SurfaceView>(R.id.surfaceview).apply {
+            setZOrderOnTop(true)
+            holder.addCallback(object : SurfaceHolder.Callback {
+                override fun surfaceCreated(p0: SurfaceHolder) {
+
+                }
+
+                override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+                    holder.unlockCanvasAndPost(holder.lockCanvas().apply {
+                        drawColor(Color.BLUE)
+                        val paint = Paint()
+                        paint.textSize = 16 * resources.displayMetrics.density
+                        paint.textAlign = Paint.Align.CENTER
+                        paint.color = Color.WHITE
+                        drawText("I'm a setZOrderOnTop(true) SurfaceView!",
+                            (width / 2).toFloat(), (height / 2).toFloat(), paint)
+                    })
+                }
+
+                override fun surfaceDestroyed(p0: SurfaceHolder) {
+
+                }
+
+            })
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index a826646..e6eabd8 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -90,17 +90,39 @@
      * and call {@link #dispatchAll()}.
      */
     public TestLooper(Clock clock) {
+        Field messageQueueUseConcurrentField = null;
+        boolean previousUseConcurrentValue = false;
+        try {
+            messageQueueUseConcurrentField = MessageQueue.class.getDeclaredField("sUseConcurrent");
+            messageQueueUseConcurrentField.setAccessible(true);
+            previousUseConcurrentValue = messageQueueUseConcurrentField.getBoolean(null);
+            // If we are using CombinedMessageQueue, we need to disable concurrent mode for testing.
+            messageQueueUseConcurrentField.set(null, false);
+        } catch (NoSuchFieldException e) {
+            // Ignore - maybe this is not CombinedMessageQueue?
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException("Reflection error constructing or accessing looper", e);
+        }
+
         try {
             mLooper = LOOPER_CONSTRUCTOR.newInstance(false);
 
-            ThreadLocal<Looper> threadLocalLooper = (ThreadLocal<Looper>) THREAD_LOCAL_LOOPER_FIELD
-                    .get(null);
+            ThreadLocal<Looper> threadLocalLooper =
+                    (ThreadLocal<Looper>) THREAD_LOCAL_LOOPER_FIELD.get(null);
             threadLocalLooper.set(mLooper);
         } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
             throw new RuntimeException("Reflection error constructing or accessing looper", e);
         }
 
         mClock = clock;
+
+        if (messageQueueUseConcurrentField != null) {
+            try {
+                messageQueueUseConcurrentField.set(null, previousUseConcurrentValue);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException("Reflection error constructing or accessing looper", e);
+            }
+        }
     }
 
     public Looper getLooper() {
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/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 7e0bbc4..3828a71 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -70,6 +70,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.Uri;
+import android.net.vcn.Flags;
 import android.net.vcn.IVcnStatusCallback;
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
 import android.net.vcn.VcnConfig;
@@ -84,6 +85,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.test.TestLooper;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -102,6 +104,7 @@
 import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -119,6 +122,8 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class VcnManagementServiceTest {
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
     private static final String TEST_PACKAGE_NAME =
             VcnManagementServiceTest.class.getPackage().getName();
@@ -288,6 +293,8 @@
         doReturn(Collections.singleton(TRANSPORT_WIFI))
                 .when(mMockDeps)
                 .getRestrictedTransports(any(), any(), any());
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CONFIG_GARBAGE_COLLECTION);
     }
 
 
@@ -438,6 +445,14 @@
             return subIds;
         }).when(snapshot).getAllSubIdsInGroup(any());
 
+        doAnswer(invocation -> {
+            final Set<ParcelUuid> subGroups = new ArraySet<>();
+            for (Entry<Integer, ParcelUuid> entry : subIdToGroupMap.entrySet()) {
+                subGroups.add(entry.getValue());
+            }
+            return subGroups;
+        }).when(snapshot).getAllSubscriptionGroups();
+
         return snapshot;
     }
 
@@ -1483,6 +1498,28 @@
     }
 
     @Test
+    public void testGarbageCollectionKeepConfigUntilNewSnapshot() throws Exception {
+        setupActiveSubscription(TEST_UUID_2);
+        startAndGetVcnInstance(TEST_UUID_2);
+
+        // Report loss of subscription from mSubMgr
+        doReturn(Collections.emptyList()).when(mSubMgr).getSubscriptionsInGroup(any());
+        triggerSubscriptionTrackerCbAndGetSnapshot(
+                TEST_UUID_2,
+                Collections.singleton(TEST_UUID_2),
+                Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_2));
+
+        assertTrue(mVcnMgmtSvc.getConfigs().containsKey(TEST_UUID_2));
+
+        // Report loss of subscription from snapshot
+        triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
+
+        mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+        mTestLooper.dispatchAll();
+        assertFalse(mVcnMgmtSvc.getConfigs().containsKey(TEST_UUID_2));
+    }
+
+    @Test
     public void testVcnCarrierConfigChangeUpdatesPolicyListener() throws Exception {
         setupActiveSubscription(TEST_UUID_2);
 
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index e045f10..4c7b25a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -223,7 +223,6 @@
         doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
         doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags();
         doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
-        doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled();
 
         doReturn(mUnderlyingNetworkController)
                 .when(mDeps)
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
index bc7ff47..441b780 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
@@ -20,7 +20,6 @@
 import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
 
 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 static org.mockito.Mockito.when;
@@ -127,8 +126,6 @@
                                 false /* isInTestMode */));
         doNothing().when(mVcnContext).ensureRunningOnLooperThread();
 
-        doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled();
-
         setupSystemService(
                 mContext,
                 mConnectivityManager,
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index 6f31d8d..e540932 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -226,7 +226,6 @@
     private void resetVcnContext(VcnContext vcnContext) {
         reset(vcnContext);
         doNothing().when(vcnContext).ensureRunningOnLooperThread();
-        doReturn(true).when(vcnContext).isFlagIpSecTransformStateEnabled();
     }
 
     // Package private for use in NetworkPriorityClassifierTest
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
index 514651e..449d93d 100644
--- a/tools/aapt2/cmd/Command.cpp
+++ b/tools/aapt2/cmd/Command.cpp
@@ -213,15 +213,28 @@
 
     bool match = false;
     for (Flag& flag : flags_) {
-      if (arg == flag.name) {
+      // Allow both "--arg value" and "--arg=value" syntax.
+      if (arg.starts_with(flag.name) &&
+          (arg.size() == flag.name.size() || (flag.num_args > 0 && arg[flag.name.size()] == '='))) {
         if (flag.num_args > 0) {
-          i++;
-          if (i >= args.size()) {
-            *out_error << flag.name << " missing argument.\n\n";
-            Usage(out_error);
-            return false;
+          if (arg.size() == flag.name.size()) {
+            i++;
+            if (i >= args.size()) {
+              *out_error << flag.name << " missing argument.\n\n";
+              Usage(out_error);
+              return 1;
+            }
+            arg = args[i];
+          } else {
+            arg.remove_prefix(flag.name.size() + 1);
+            // Disallow empty arguments after '='.
+            if (arg.empty()) {
+              *out_error << flag.name << " has empty argument.\n\n";
+              Usage(out_error);
+              return 1;
+            }
           }
-          flag.action(args[i]);
+          flag.action(arg);
         } else {
           flag.action({});
         }
diff --git a/tools/aapt2/cmd/Command_test.cpp b/tools/aapt2/cmd/Command_test.cpp
index 7aa1aa01..20d87e0 100644
--- a/tools/aapt2/cmd/Command_test.cpp
+++ b/tools/aapt2/cmd/Command_test.cpp
@@ -19,6 +19,7 @@
 #include "test/Test.h"
 
 using ::testing::Eq;
+using namespace std::literals;
 
 namespace aapt {
 
@@ -94,4 +95,27 @@
 }
 #endif
 
+TEST(CommandTest, OptionsWithValues) {
+  TestCommand command;
+  std::string flag;
+  command.AddRequiredFlag("--flag", "", &flag);
+
+  ASSERT_EQ(0, command.Execute({"--flag"s, "1"s}, &std::cerr));
+  EXPECT_STREQ("1", flag.c_str());
+
+  ASSERT_EQ(0, command.Execute({"--flag=1"s}, &std::cerr));
+  EXPECT_STREQ("1", flag.c_str());
+
+  ASSERT_EQ(0, command.Execute({"--flag"s, "=2"s}, &std::cerr));
+  EXPECT_STREQ("=2", flag.c_str());
+
+  ASSERT_EQ(0, command.Execute({"--flag"s, "--flag"s}, &std::cerr));
+  EXPECT_STREQ("--flag", flag.c_str());
+
+  EXPECT_NE(0, command.Execute({"--flag"s}, &std::cerr));
+  EXPECT_NE(0, command.Execute({"--flag="s}, &std::cerr));
+  EXPECT_NE(0, command.Execute({"--flag1=2"s}, &std::cerr));
+  EXPECT_NE(0, command.Execute({"--flag1"s, "2"s}, &std::cerr));
+}
+
 }  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 52372fa..a5e18d35 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -605,8 +605,9 @@
     }
 
     // Write the crunched PNG.
-    if (!android::WritePng(image.get(), nine_patch.get(), &crunched_png_buffer_out, {},
-                           &source_diag, context->IsVerbose())) {
+    if (!android::WritePng(image.get(), nine_patch.get(), &crunched_png_buffer_out,
+                           {.compression_level = options.png_compression_level_int}, &source_diag,
+                           context->IsVerbose())) {
       return false;
     }
 
@@ -924,6 +925,19 @@
     }
   }
 
+  if (!options_.png_compression_level) {
+    options_.png_compression_level_int = 9;
+  } else {
+    if (options_.png_compression_level->size() != 1 ||
+        options_.png_compression_level->front() < '0' ||
+        options_.png_compression_level->front() > '9') {
+      context.GetDiagnostics()->Error(
+          android::DiagMessage() << "PNG compression level should be a number in [0..9] range");
+      return 1;
+    }
+    options_.png_compression_level_int = options_.png_compression_level->front() - '0';
+  }
+
   return Compile(&context, file_collection.get(), archive_writer.get(), options_);
 }
 
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 70c8791..e244546 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -47,6 +47,8 @@
   bool verbose = false;
   std::optional<std::string> product_;
   FeatureFlagValues feature_flag_values;
+  std::optional<std::string> png_compression_level;
+  int png_compression_level_int = 9;
 };
 
 /** Parses flags and compiles resources to be used in linking.  */
@@ -65,6 +67,9 @@
     AddOptionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
         "(en-XA and ar-XB)", &options_.pseudolocalize);
     AddOptionalSwitch("--no-crunch", "Disables PNG processing", &options_.no_png_crunch);
+    AddOptionalFlag("--png-compression-level",
+                    "Set the zlib compression level for crunched PNG images, [0-9], 9 by default.",
+                    &options_.png_compression_level);
     AddOptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
         &options_.legacy_mode);
     AddOptionalSwitch("--preserve-visibility-of-styleables",
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 2285880..108942e 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -368,7 +368,7 @@
     }
 
     interface ProtologViewerConfigBuilder {
-        fun build(statements: Map<LogCall, Long>): ByteArray
+        fun build(groups: Collection<LogGroup>, statements: Map<LogCall, Long>): ByteArray
     }
 
     private fun viewerConf(command: CommandOptions) {
@@ -416,7 +416,7 @@
         }
 
         val outFile = injector.fileOutputStream(command.viewerConfigFileNameArg)
-        outFile.write(configBuilder.build(logCallRegistry.getStatements()))
+        outFile.write(configBuilder.build(groups.values, logCallRegistry.getStatements()))
         outFile.close()
     }
 
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigJsonBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigJsonBuilder.kt
index 7714db2..16a3d7c 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigJsonBuilder.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigJsonBuilder.kt
@@ -21,8 +21,7 @@
 import java.io.StringWriter
 
 class ViewerConfigJsonBuilder : ProtoLogTool.ProtologViewerConfigBuilder {
-    override fun build(statements: Map<ProtoLogTool.LogCall, Long>): ByteArray {
-        val groups = statements.map { it.key.logGroup }.toSet()
+    override fun build(groups: Collection<LogGroup>, statements: Map<ProtoLogTool.LogCall, Long>): ByteArray {
         val stringWriter = StringWriter()
         val writer = JsonWriter(stringWriter)
         writer.setIndent("  ")
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
index 245e802..de85411 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
@@ -28,10 +28,14 @@
      * @return a byte array of a ProtoLogViewerConfig proto message encoding all the viewer
      * configurations mapping protolog hashes to message information and log group information.
      */
-    override fun build(statements: Map<ProtoLogTool.LogCall, Long>): ByteArray {
+    override fun build(groups: Collection<LogGroup>, statements: Map<ProtoLogTool.LogCall, Long>): ByteArray {
         val configBuilder = ProtoLogViewerConfig.newBuilder()
 
-        val groups = statements.map { it.key.logGroup }.toSet()
+        // TODO(b/373754057): We are passing all the groups now, because some groups might only be
+        //  used by Kotlin code that is not processed, but for group that get enabled to log to
+        //  logcat we try and load the viewer configurations for this group, so the group must exist
+        //  in the viewer config. Once Kotlin is pre-processed or this logic changes we should only
+        //  use the groups that are actually used as an optimization.
         val groupIds = mutableMapOf<LogGroup, Int>()
         groups.forEach {
             groupIds.putIfAbsent(it, groupIds.size + 1)
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
index d27ae88..1a20d4c 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
@@ -36,6 +36,8 @@
         private val GROUP_DISABLED = LogGroup("DEBUG_GROUP", false, true, TAG2)
         private val GROUP_TEXT_DISABLED = LogGroup("DEBUG_GROUP", true, false, TAG2)
         private const val PATH = "/tmp/test.java"
+
+        private val GROUPS = listOf(GROUP1, GROUP2, GROUP3)
     }
 
     private val configBuilder = ViewerConfigJsonBuilder()
@@ -53,7 +55,7 @@
                 LogCall(TEST3.messageString, LogLevel.ERROR, GROUP3, PATH)))
 
         val parsedConfig = parseConfig(
-            configBuilder.build(logCallRegistry.getStatements()).toString(Charsets.UTF_8))
+            configBuilder.build(GROUPS, logCallRegistry.getStatements()).toString(Charsets.UTF_8))
         assertEquals(3, parsedConfig.size)
         assertEquals(TEST1, parsedConfig[CodeUtils.hash(PATH,
 	           TEST1.messageString, LogLevel.INFO, GROUP1)])
@@ -72,7 +74,7 @@
                 LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH)))
 
         val parsedConfig = parseConfig(
-            configBuilder.build(logCallRegistry.getStatements()).toString(Charsets.UTF_8))
+            configBuilder.build(GROUPS, logCallRegistry.getStatements()).toString(Charsets.UTF_8))
         assertEquals(1, parsedConfig.size)
         assertEquals(TEST1, parsedConfig[CodeUtils.hash(PATH, TEST1.messageString,
             LogLevel.INFO, GROUP1)])
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt
new file mode 100644
index 0000000..74a8de7
--- /dev/null
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigProtoBuilderTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.protolog.tool
+
+import com.android.internal.protolog.common.LogLevel
+import com.android.protolog.tool.ProtoLogTool.LogCall
+import com.google.common.truth.Truth
+import org.junit.Test
+import perfetto.protos.PerfettoTrace.ProtoLogViewerConfig
+
+class ViewerConfigProtoBuilderTest {
+    companion object {
+        private val TAG1 = "WM_TEST"
+        private val TAG2 = "WM_DEBUG"
+
+        private val TEST1 = ViewerConfigParser.ConfigEntry("test1", LogLevel.INFO.name,
+            TAG1
+        )
+        private val TEST2 = ViewerConfigParser.ConfigEntry("test2", LogLevel.DEBUG.name,
+            TAG2
+        )
+
+        private val GROUP1 = LogGroup("TEST_GROUP", true, true, TAG1)
+        private val GROUP2 = LogGroup("DEBUG_GROUP", true, true, TAG2)
+        private val GROUP3 = LogGroup("UNUSED_GROUP", true, true, TAG1)
+
+        private val GROUPS = listOf(
+            GROUP1,
+            GROUP2,
+            GROUP3
+        )
+
+        private const val PATH = "/tmp/test.java"
+    }
+
+    @Test
+    fun includesUnusedProtoLogGroups() {
+        // Added because of b/373754057. This test might need to be removed in the future.
+
+        val configBuilder = ViewerConfigProtoBuilder()
+
+        val logCallRegistry = ProtoLogTool.LogCallRegistry()
+        logCallRegistry.addLogCalls(listOf(
+            LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
+            LogCall(TEST2.messageString, LogLevel.INFO, GROUP2, PATH),
+        ))
+
+        val rawProto = configBuilder.build(GROUPS, logCallRegistry.getStatements())
+
+        val viewerConfig = ProtoLogViewerConfig.parseFrom(rawProto)
+        Truth.assertThat(viewerConfig.groupsCount).isEqualTo(GROUPS.size)
+        Truth.assertThat(viewerConfig.messagesCount).isLessThan(GROUPS.size)
+    }
+}
\ No newline at end of file
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
index 590f719..e6d0a3d 100644
--- a/tools/systemfeatures/Android.bp
+++ b/tools/systemfeatures/Android.bp
@@ -58,6 +58,7 @@
         "junit",
         "objenesis",
         "mockito",
+        "systemfeatures-gen-lib",
         "truth",
     ],
 }
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index 196b5e7..1abe77f 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -71,7 +71,7 @@
         println("Usage: SystemFeaturesGenerator <outputClassName> [options]")
         println(" Options:")
         println("  --readonly=true|false    Whether to encode features as build-time constants")
-        println("  --feature=\$NAME:\$VER   A feature+version pair, where \$VER can be:")
+        println("  --feature=\$NAME:\$VER     A feature+version pair, where \$VER can be:")
         println("                             * blank/empty == undefined (variable API)")
         println("                             * valid int   == enabled   (constant API)")
         println("                             * UNAVAILABLE == disabled  (constant API)")
@@ -89,6 +89,17 @@
     /** Main entrypoint for build-time system feature codegen. */
     @JvmStatic
     fun main(args: Array<String>) {
+        generate(args, System.out)
+    }
+
+    /**
+     * Simple API entrypoint for build-time system feature codegen.
+     *
+     * Note: Typically this would be implemented in terms of a proper Builder-type input argument,
+     * but it's primarily used for testing as opposed to direct production usage.
+     */
+    @JvmStatic
+    fun generate(args: Array<String>, output: Appendable) {
         if (args.size < 1) {
             usage()
             return
@@ -155,7 +166,7 @@
             .addFileComment("This file is auto-generated. DO NOT MODIFY.\n")
             .addFileComment("Args: ${args.joinToString(" \\\n           ")}")
             .build()
-            .writeTo(System.out)
+            .writeTo(output)
     }
 
     /*
@@ -171,12 +182,27 @@
         return when (featureArgs.getOrNull(1)) {
             null, "" -> FeatureInfo(name, null, readonly = false)
             "UNAVAILABLE" -> FeatureInfo(name, null, readonly = true)
-            else -> FeatureInfo(name, featureArgs[1].toIntOrNull(), readonly = true)
+            else -> {
+                val featureVersion =
+                    featureArgs[1].toIntOrNull()
+                        ?: throw IllegalArgumentException(
+                            "Invalid feature version input for $name: ${featureArgs[1]}"
+                        )
+                FeatureInfo(name, featureArgs[1].toInt(), readonly = true)
+            }
         }
     }
 
     private fun parseFeatureName(name: String): String =
-        if (name.startsWith("FEATURE_")) name else "FEATURE_$name"
+        when {
+            name.startsWith("android") ->
+                throw IllegalArgumentException(
+                    "Invalid feature name input: \"android\"-namespaced features must be " +
+                    "provided as PackageManager.FEATURE_* suffixes, not raw feature strings."
+                )
+            name.startsWith("FEATURE_") -> name
+            else -> "FEATURE_$name"
+        }
 
     /*
      * Adds per-feature query methods to the class with the form:
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java
new file mode 100644
index 0000000..f8c585d
--- /dev/null
+++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemfeatures;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.io.IOException;
+
+// Note: This is a very simple argument test to validate certain behaviors for
+// invalid arguments. Correctness and validity is largely exercised by
+// SystemFeaturesGeneratorTest.
+@RunWith(JUnit4.class)
+public class SystemFeaturesGeneratorApiTest {
+
+    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock private Appendable mOut;
+
+    @Test
+    public void testEmpty() throws IOException {
+        final String[] args = new String[] {};
+        // This should just print the commandline and return.
+        SystemFeaturesGenerator.generate(args, mOut);
+        verify(mOut, never()).append(any());
+    }
+
+    @Test
+    public void testBasic() throws IOException {
+        final String[] args = new String[] {
+            "com.foo.Features",
+            "--feature=TELEVISION:0",
+        };
+        SystemFeaturesGenerator.generate(args, mOut);
+        verify(mOut, atLeastOnce()).append(any());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidFeatureVersion() throws IOException {
+        final String[] args = new String[] {
+            "com.foo.Features",
+            "--feature=TELEVISION:blarg",
+        };
+        SystemFeaturesGenerator.generate(args, mOut);
+        verify(mOut, never()).append(any());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidFeatureNameFromAndroidNamespace() throws IOException {
+        final String[] args = new String[] {
+            "com.foo.Features",
+            "--feature=android.hardware.doesntexist:0",
+        };
+        SystemFeaturesGenerator.generate(args, mOut);
+        verify(mOut, never()).append(any());
+    }
+}